Browse Source

细节优化:列表搜索过滤/数据源定义校验提示/图表退出修改保存提示

zhuth 7 years ago
parent
commit
c3b4e24c15

+ 1 - 0
package.json

@@ -7,6 +7,7 @@
     "precommit": "npm run lint"
   },
   "dependencies": {
+    "ant-design-pro": "^2.0.0-beta.2",
     "antd": "^3.0.0",
     "app": "^0.1.0",
     "braft-editor": "^1.9.8",

+ 12 - 21
src/components/chart/chooseDataSourceBox.jsx

@@ -1,8 +1,8 @@
 import React from 'react'
-import { Modal, Table, Radio, Tag, message } from 'antd'
+import { Modal, Table, Radio, message } from 'antd'
 import { connect } from 'dva'
-import '../../models/dataSource'
 import './chooseDataSourceBox.less'
+import { dateFormat } from '../../utils/baseUtils'
 
 class ChooseDataSourceBox extends React.Component {
     constructor(props) {
@@ -52,29 +52,21 @@ class ChooseDataSourceBox extends React.Component {
                 </div>
             }
         }, {
-            title: '标签',
-            dataIndex: 'tags',
-            key: 'tag',
-            width: 150,
-            render: (text, record) => {
-                text=text.join(',');
-                let tags = text ? text.split(',').map((t, i) => {
-                    return <Tag className='datasource-tag' key={i}>{t}</Tag>
-                }) : '';
-                return (<div>
-                    {tags}
-                </div>)
-            }
+            title: '创建人',
+            dataIndex: 'creator',
+            key: 'creator',
+            width: 100
+        }, {
+            title: '创建时间',
+            dataIndex: 'createTime',
+            key: 'createTime',
+            width: 120,
+            render: (text) => dateFormat(new Date(text), 'yyyy-MM-dd')
         }, {
             title: '说明',
             dataIndex: 'description',
             key: 'description',
             width: 200
-        }, {
-            title: '图表',
-            dataIndex: 'chartSum',
-            key: 'chartSum',
-            width: 80
         }];
 
         return (
@@ -94,7 +86,6 @@ class ChooseDataSourceBox extends React.Component {
                     size='small'
                     scroll={{x: false, y: 471}}
                     pagination={false}
-                    showHeader={false}
                     onRow={(record) => {
                         return {
                             onClick: () => {

+ 6 - 0
src/components/chart/chooseDataSourceBox.less

@@ -4,5 +4,11 @@
         padding: 0;
         max-height: 50vh;
         overflow-y: auto;
+        .choosedatasource-table {
+            .ant-table-header {
+                overflow-y: hidden;
+                margin-right: 6px;
+            }
+        }
     }
 }

+ 19 - 10
src/components/chart/list.jsx

@@ -1,10 +1,11 @@
 import React from 'react'
 import { Layout, Button, Icon, Input, Menu, Dropdown, Card, Col, Row } from 'antd'
 import { connect } from 'dva'
-import '../../models/chart'
 import './list.less'
 import ChooseDataSourceBox from './chooseDataSourceBox'
 import { dateFormat } from '../../utils/baseUtils'
+import Ellipsis from 'ant-design-pro/lib/Ellipsis'
+import 'ant-design-pro/dist/ant-design-pro.css'
 const { Content } = Layout
 const { Search } = Input
 const CardGrid = Card.Grid
@@ -41,8 +42,9 @@ class ChartList extends React.Component {
     }
 
     generateCard() {
-        const dispatch = this.props.dispatch;
-        const list = this.props.chart.list;
+        const { chart, dispatch } = this.props;
+        const list = chart.list;
+        const filterLabel = chart.filterLabel;
 
         const operationMenu = (
             <Menu className='menu-operation' onClick={() => {
@@ -54,7 +56,10 @@ class ChartList extends React.Component {
             </Menu>
         )
 
-        let cards = list.sort((a, b) => {
+        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)
         }).map( (l, i) => (
             <CardGrid className='chart-card' key={i} onClick={() => {
@@ -70,17 +75,19 @@ class ChartList extends React.Component {
                         </Row>
                     }
                     cover={
-                        <Col style={{ padding: '10px' }}>
-                            <Row style={{ height: '164px' }} onClick={() => {
+                        <Col className='cover-body'>
+                            <Row className='thumb' onClick={() => {
                                 dispatch({ type: 'chartDesigner/reset' });
                                 dispatch({ type: 'main/redirect', path: '/chart/' + l.code });
                             }}>
                                 <img alt='' style={{ height: '100px', marginTop: '10%' }} src='https://test-feapp.oss-cn-beijing.aliyuncs.com/feapp/master_a_20180711/images/chart/empty_column_chart.svg'></img>
                             </Row>
-                            <Row type='flex' justify='end' align='bottom'>
+                            <Row className='desc'>
+                                <Ellipsis tooltip={l.description.length > 16} lines={2}>{l.description}</Ellipsis>
+                            </Row>
+                            <Row className='footer' type='flex' justify='end' align='bottom'>
                                 <Col style={{ textAlign: 'left' }} span={22}>
-                                    <Row>{l.creator}</Row>
-                                    <Row>{dateFormat(l.createTime, 'yyyy-MM-dd')}</Row>
+                                    <Row>{l.creator} {dateFormat(l.createTime, 'yyyy-MM-dd')}</Row>
                                 </Col>
                                 <Col span={2} style={{ textAlign: 'right' }}>
                                     <Dropdown overlay={operationMenu} trigger={['click']}>
@@ -112,7 +119,9 @@ class ChartList extends React.Component {
                             <Col style={{ padding: '0 5px' }}>
                                 <Search
                                     placeholder="请输入关键字"
-                                    onSearch={value => console.log(value)}
+                                    onChange={e => {
+                                        dispatch({ type: 'chart/setFilterLabel', label: e.target.value });
+                                    }}
                                 />
                             </Col>
                             <Col >

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

@@ -29,6 +29,19 @@
             .ant-card-cover {
                 height: 200px;
                 cursor: pointer;
+                .cover-body {
+                    padding: 10px;
+                    .thumb {
+                        height: 132px;
+                    }
+                    .desc {
+                        text-align: left;
+                        height: 41px;
+                    }
+                    .footer {
+                        margin-top: 6px;
+                    }
+                }
                 .ant-row-flex-bottom {
                     cursor: default;
                     .anticon {
@@ -44,4 +57,8 @@
     .ant-dropdown-menu-item .anticon {
         margin-right: 6px;
     }
+}
+.ant-tooltip-inner {
+    background-color: rgba(255, 255, 255, .9);
+    color: #666
 }

+ 4 - 5
src/components/chartDesigner/header.jsx

@@ -18,8 +18,7 @@ class Header extends React.Component {
     }
 
     render() {
-        const { past, chartDesigner, dispatch } = this.props;
-
+        const { chartDesigner, dispatch } = this.props;
         return (
             <div className='header'>
                 <div className='header-item toolbar-back'>
@@ -45,7 +44,7 @@ class Header extends React.Component {
                         cancelText="不必了"
                     >
                         <Button onClick={() => {
-                            if(past.length > 0) {
+                            if(chartDesigner.dirty) {
                                 this.setState({
                                     visibleConfirm: true
                                 });
@@ -62,6 +61,7 @@ class Header extends React.Component {
                         }else {
                             dispatch({ type: 'chartDesigner/remoteAdd' });
                         }
+                        dispatch({ type: 'chartDesigner/setDirty', dirty: false });
                     }}><Icon type='save' />保存</Button>
                 </div>
                 <div className='header-item toolbar-title'>
@@ -97,7 +97,6 @@ class Header extends React.Component {
 
 function mapStateToProps(state) {
     const chartDesigner = state.present.chartDesigner;
-    const past = state.past;
-    return { past, chartDesigner }
+    return { chartDesigner }
 }
 export default connect(mapStateToProps)(Header);

+ 4 - 7
src/components/chartDesigner/sections/otherConfigForm.jsx

@@ -12,14 +12,11 @@ const OtherConfigForm = ({ chartDesigner, dispatch, formItemLayout }) => {
             <FormItem label='备注' {...formItemLayout}>
                 <InputTextArea
                     className='inputarea-description'
-                    value={chartDesigner.otherConfig.description}
+                    value={chartDesigner.description}
                     autosize={{ minRows: 2, maxRows: 6 }}
-                    // onChange={(e) => {
-                    //     dispatch({ type: 'chartDesigner/setField', name: 'otherConfig', value: {
-                    //         ...chartDesigner.otherConfig,
-                    //         description: e.target.value
-                    //     } });
-                    // }}
+                    onChange={(e) => {
+                        dispatch({ type: 'chartDesigner/setField', name: 'description', value: e.target.value });
+                    }}
                 />
             </FormItem>
         </Form>

+ 6 - 6
src/components/datasource/baseConfig.jsx

@@ -33,7 +33,7 @@ const DataSourceBaseConfig = ({ dataSource, dataConnect, dispatch, mode }) => {
                         <Divider orientation="left">连接配置</Divider>
                         <FormItem label='数据库类型' {...formItemLayout}>
                             <Select
-                                disabled
+                                disabled={true}
                                 value={dataSource.newOne.dbType}
                                 onChange={(value) => {
                                     dispatch({ type: 'dataSource/setNewModelField', name: 'dbType', value: value} );
@@ -60,7 +60,7 @@ const DataSourceBaseConfig = ({ dataSource, dataConnect, dispatch, mode }) => {
                                     wrapperCol: { span: 19 }
                                 }}>
                                     <Input
-                                        disabled
+                                        disabled={true}
                                         value={dataSource.newOne.address}
                                         onChange={(e) => {
                                             dispatch({ type: 'dataSource/setNewModelField', name: 'address', value: e.target.value });
@@ -74,7 +74,7 @@ const DataSourceBaseConfig = ({ dataSource, dataConnect, dispatch, mode }) => {
                                     wrapperCol: { span: 12 }
                                 }}>
                                     <InputNumber
-                                        disabled
+                                        disabled={true}
                                         value={dataSource.newOne.port}
                                         onChange={(value) => {
                                             dispatch({ type: 'dataSource/setNewModelField', name: 'port', value: value });
@@ -85,7 +85,7 @@ const DataSourceBaseConfig = ({ dataSource, dataConnect, dispatch, mode }) => {
                         </Row>
                         <FormItem label='数据库名' {...formItemLayout}>
                             <Input
-                                disabled
+                                disabled={true}
                                 value={dataSource.newOne.dbName}
                                 onChange={(e) => {
                                     dispatch({ type: 'dataSource/setNewModelField', name: 'dbName', value: e.target.value });
@@ -99,7 +99,7 @@ const DataSourceBaseConfig = ({ dataSource, dataConnect, dispatch, mode }) => {
                                     wrapperCol: { span: 16 }
                                 }}>
                                     <Input
-                                        disabled
+                                        disabled={true}
                                         value={dataSource.newOne.userName}
                                         onChange={(e) => {
                                             dispatch({ type: 'dataSource/setNewModelField', name: 'userName', value: e.target.value });
@@ -113,7 +113,7 @@ const DataSourceBaseConfig = ({ dataSource, dataConnect, dispatch, mode }) => {
                                     wrapperCol: { span: 16 }
                                 }}>
                                     <Input
-                                        disabled
+                                        disabled={true}
                                         // value={dataSource.newOne.password}
                                         onChange={(e) => {
                                             let value = e.target.value;

+ 6 - 7
src/components/datasource/columnConfig.jsx

@@ -1,7 +1,6 @@
 import React from 'react'
 import { Form, Input, Button, Select, Table, Checkbox, Switch, Divider } from 'antd'
 import { connect } from 'dva'
-import '../../models/dataSource'
 import COLUMN_TYPE from './columnType.json'
 const FormItem = Form.Item
 const SelectOption = Select.Option
@@ -21,7 +20,7 @@ class DataSourceColumnConfig extends React.Component {
 
     render() {
 
-        const { dataSource, dispatch } = this.props;
+        const { dataSource, dataConnect, dispatch } = this.props;
 
         const columns = [{
             title: <div><Checkbox
@@ -185,7 +184,6 @@ class DataSourceColumnConfig extends React.Component {
             }
         }];
 
-        console.log(dataSource.newOne.type);
         return (
             <div>
                 {
@@ -195,7 +193,8 @@ class DataSourceColumnConfig extends React.Component {
                                 <Divider orientation="left">数据对象</Divider>
                                 <FormItem className='textarea-target'>
                                     <Input.TextArea
-                                        placeholder='输入表名或查询SQL'
+                                        disabled={!dataSource.newOne.address}
+                                        placeholder={dataConnect.selected ? '输入表名或查询SQL' : '请返回上一步选择数据库连接'}
                                         autosize={{ minRows: 3 }}
                                         value={dataSource.newOne.target}
                                         onChange={(e) => {
@@ -204,7 +203,7 @@ class DataSourceColumnConfig extends React.Component {
                                     />
                                 </FormItem>
                                 <div className='buttons'>
-                                    <Button onClick={() => {
+                                    <Button disabled={!dataConnect.selected} onClick={() => {
                                         dispatch({ type: 'dataSource/importNewModelColumns' })
                                     }}>刷新</Button>
                                 </div>
@@ -226,8 +225,8 @@ class DataSourceColumnConfig extends React.Component {
     }
 }
 
-function mapStateToProps({ present: { dataSource } }) {
-    return { dataSource }
+function mapStateToProps({ present: { dataSource, dataConnect } }) {
+    return { dataSource, dataConnect }
 }
 
 export default connect(mapStateToProps)(DataSourceColumnConfig);

+ 159 - 126
src/components/datasource/dataConnectBox.jsx

@@ -1,144 +1,177 @@
 import React from 'react'
-import { Modal, Form, Row, Col, Input, InputNumber, Select } from 'antd'
+import { Modal, Form, Row, Col, Input, InputNumber, Select, Button, Icon } from 'antd'
 import { connect } from 'dva'
-import '../../models/dataConnect'
+import './dataConnectBox.less'
 const FormItem = Form.Item
 const SelectOption = Select.Option
 
-const DataConnectBox = ({operation, dispatch, dataConnect, visibleBox, hideBox, form}) => {
-    const formItemLayout = {
-        labelCol: { span: 4 },
-        wrapperCol: { span: 20 },
-    };
+class DataConnectBox extends React.Component {
 
-    const okHandler = (model) => {
+    hideBox() {
+        const { dispatch } = this.props;
+        dispatch({ type: 'dataConnect/setNewModelField', name: 'visibleBox', value: false });
+    }
+
+    okHandler() {
+        const { dispatch, dataConnect} = this.props;
+        const operation = dataConnect.newOne.boxOperation;
         if(operation === 'create') {
             dispatch({ type: 'dataConnect/remoteAdd' });
         }else if(operation === 'modify') {
-            dispatch({ type: 'dataConnect/remoteModify', code: model.code });
+            dispatch({ type: 'dataConnect/remoteModify', code: dataConnect.newOne.code });
         }
-        hideBox();
     }
 
-    return (
-        <Modal
-            className='dataconnect-box'
-            title={`${operation==='create'?'新增':'修改'}数据库连接配置`}
-            visible={visibleBox}
-            onOk={() => {okHandler(dataConnect.newOne)}}
-            onCancel={hideBox}
-            maskClosable={false}
-            destroyOnClose={true}
-        >
-            <Form size='small'>
-                <FormItem label='连接配置名' {...formItemLayout}>
-                    <Input
-                        value={dataConnect.newOne.name}
-                        onChange={(e) => { dispatch({ type: 'dataConnect/setNewModelField', name: 'name', value: e.target.value }) }}>
-                    </Input>
-                </FormItem>
-                <FormItem label='数据库类型' {...formItemLayout}>
-                    <Select
-                        value={dataConnect.newOne.dbType}
-                        onChange={(value) => {
-                            dispatch({ type: 'dataConnect/setNewModelField', name: 'dbType', value: value} );
-                        }}
+    render() {
+        const { dispatch, dataConnect } = this.props;
+
+        const formItemLayout = {
+            labelCol: { span: 4 },
+            wrapperCol: { span: 20 },
+        };
+
+        return (
+            <Modal
+                className='dataconnect-box'
+                title={`${dataConnect.newOne.boxOperation === 'create'?'新建':'修改'}数据库连接`}
+                visible={dataConnect.newOne.visibleBox}
+                onCancel={() => { this.hideBox() }}
+                maskClosable={false}
+                destroyOnClose={true}
+                footer={
+                    <Row>
+                        <Col className='validatemessage' span={14}>
+                            {dataConnect.newOne.invalid ? '数据连接配置有误!' : ''}
+                        </Col>
+                        <Col span={10}>
+                            <Button onClick={() => {this.hideBox()}}>取 消</Button>
+                            <Button className={dataConnect.newOne.validating ? 'ant-btn-loading' : ''} type="primary" onClick={() => {this.okHandler()}}>
+                                {dataConnect.newOne.validating ? (<Icon type='loading' />) : ''}确 定
+                            </Button>
+                        </Col>
+                    </Row>
+                }
+            >
+                <Form size='small'>
+                    <FormItem
+                        label='连接名'
+                        {...formItemLayout}
+                        validateStatus={dataConnect.validInfo.name ? dataConnect.validInfo.name.validateStatus : ''}
+                        help={dataConnect.validInfo.name ? dataConnect.validInfo.name.help : ''}
                     >
-                        <SelectOption value='oracle'>
-                            ORACLE
-                        </SelectOption>
-                        <SelectOption value='mysql'>
-                            MYSQL
-                        </SelectOption>
-                        <SelectOption value='sqlserver'>
-                            SQLSERVER
-                        </SelectOption>
-                        <SelectOption value='sqlite'>
-                            SQLITE
-                        </SelectOption>
-                    </Select>
-                </FormItem>
-                <Row>
-                    <Col span={19}>
-                        <FormItem label='数据库地址' {...{
-                            labelCol: { span: 5 },
-                            wrapperCol: { span: 19 }
-                        }}>
-                            <Input
-                                value={dataConnect.newOne.address}
-                                onChange={(e) => {
-                                    dispatch({ type: 'dataConnect/setNewModelField', name: 'address', value: e.target.value });
-                                }}
-                            />
-                        </FormItem>
-                    </Col>
-                    <Col span={5}>
-                        <FormItem className='input-port' label='端口' {...{
-                            labelCol: { span: 12 },
-                            wrapperCol: { span: 12 }
-                        }}>
-                            <InputNumber
-                                value={dataConnect.newOne.port}
-                                onChange={(value) => {
-                                    dispatch({ type: 'dataConnect/setNewModelField', name: 'port', value: value });
-                                }}
-                            />
-                        </FormItem>
-                    </Col>
-                </Row>
-                <FormItem label='数据库名' {...formItemLayout}>
-                    <Input
-                        value={dataConnect.newOne.dbName}
-                        onChange={(e) => {
-                            dispatch({ type: 'dataConnect/setNewModelField', name: 'dbName', value: e.target.value });
-                        }}
-                    />
-                </FormItem>
-                <Row>
-                    <Col span={12}>
-                        <FormItem label='用户名' {...{
-                            labelCol: { span: 8 },
-                            wrapperCol: { span: 16 }
-                        }}>
-                            <Input
-                                value={dataConnect.newOne.userName}
-                                onChange={(e) => {
-                                    dispatch({ type: 'dataConnect/setNewModelField', name: 'userName', value: e.target.value });
-                                }}
-                            />
-                        </FormItem>
-                    </Col>
-                    <Col span={12}>
-                        <FormItem label='密码' {...{
-                            labelCol: { span: 8 },
-                            wrapperCol: { span: 16 }
-                        }}>
-                            <Input
-                                type='password'
-                                value={dataConnect.newOne.password}
-                                onChange={(e) => {
-                                    dispatch({ type: 'dataConnect/setNewModelField', name: 'password', value: e.target.value });
-                                }}
-                            />
-                        </FormItem>
-                    </Col>
-                </Row>
-                <FormItem className='textarea-desc' label='说明' {...formItemLayout}>
-                    <Input.TextArea
-                        autosize={{ minRows: 2 }}
-                        value={dataConnect.newOne.description}
-                        onChange={(e) => {
-                            dispatch({ type: 'dataConnect/setNewModelField', name: 'description', value: e.target.value });
-                        }}
-                    />
-                </FormItem>
-            </Form>
-        </Modal>
-    )
+                        <Input
+                            placeholder="输入数据库连接名称"
+                            value={dataConnect.newOne.name}
+                            onChange={(e) => { dispatch({ type: 'dataConnect/setNewModelField', name: 'name', value: e.target.value }) }}
+                        >
+                        </Input>
+                    </FormItem>
+                    <FormItem label='数据库类型' {...formItemLayout}>
+                        <Select
+                            placeholder="选择数据库类型"
+                            value={dataConnect.newOne.dbType}
+                            onChange={(value) => {
+                                dispatch({ type: 'dataConnect/setNewModelField', name: 'dbType', value: value} );
+                            }}
+                        >
+                            <SelectOption value='oracle'>
+                                ORACLE
+                            </SelectOption>
+                            <SelectOption value='mysql'>
+                                MYSQL
+                            </SelectOption>
+                            <SelectOption value='sqlserver'>
+                                SQLSERVER
+                            </SelectOption>
+                            <SelectOption value='sqlite'>
+                                SQLITE
+                            </SelectOption>
+                        </Select>
+                    </FormItem>
+                    <Row>
+                        <Col span={19}>
+                            <FormItem label='数据库地址' {...{
+                                labelCol: { span: 5 },
+                                wrapperCol: { span: 19 }
+                            }}>
+                                <Input
+                                    placeholder="格式:192.168.1.1"
+                                    value={dataConnect.newOne.address}
+                                    onChange={(e) => {
+                                        dispatch({ type: 'dataConnect/setNewModelField', name: 'address', value: e.target.value });
+                                    }}
+                                />
+                            </FormItem>
+                        </Col>
+                        <Col span={5}>
+                            <FormItem className='input-port' label='端口' {...{
+                                labelCol: { span: 12 },
+                                wrapperCol: { span: 12 }
+                            }}>
+                                <InputNumber
+                                    value={dataConnect.newOne.port}
+                                    onChange={(value) => {
+                                        dispatch({ type: 'dataConnect/setNewModelField', name: 'port', value: value });
+                                    }}
+                                />
+                            </FormItem>
+                        </Col>
+                    </Row>
+                    <FormItem label='数据库名(SID)' {...formItemLayout}>
+                        <Input
+                            value={dataConnect.newOne.dbName}
+                            onChange={(e) => {
+                                dispatch({ type: 'dataConnect/setNewModelField', name: 'dbName', value: e.target.value });
+                            }}
+                        />
+                    </FormItem>
+                    <Row>
+                        <Col span={12}>
+                            <FormItem label='用户名' {...{
+                                labelCol: { span: 8 },
+                                wrapperCol: { span: 16 }
+                            }}>
+                                <Input
+                                    value={dataConnect.newOne.userName}
+                                    onChange={(e) => {
+                                        dispatch({ type: 'dataConnect/setNewModelField', name: 'userName', value: e.target.value });
+                                    }}
+                                />
+                            </FormItem>
+                        </Col>
+                        <Col span={12}>
+                            <FormItem label='密码' {...{
+                                labelCol: { span: 8 },
+                                wrapperCol: { span: 16 }
+                            }}>
+                                <Input
+                                    type='password'
+                                    // value={dataConnect.newOne.password}
+                                    onChange={(e) => {
+                                        dispatch({ type: 'dataConnect/setNewModelField', name: 'password', value: e.target.value });
+                                    }}
+                                />
+                            </FormItem>
+                        </Col>
+                    </Row>
+                    <FormItem className='textarea-desc' label='说明' {...formItemLayout}>
+                        <Input.TextArea
+                            autosize={{ minRows: 2 }}
+                            value={dataConnect.newOne.description}
+                            onChange={(e) => {
+                                dispatch({ type: 'dataConnect/setNewModelField', name: 'description', value: e.target.value });
+                            }}
+                        />
+                    </FormItem>
+                </Form>
+            </Modal>
+        )
+    }
 }
 
-function mapStateToProps({ present: { dataConnect } }) {
-    return { dataConnect: dataConnect };
+function mapStateToProps(state) {
+    const dataConnect = state.present.dataConnect;
+    return { dataConnect };
 }
 
 export default connect(mapStateToProps)(DataConnectBox)

+ 13 - 0
src/components/datasource/dataConnectBox.less

@@ -0,0 +1,13 @@
+.dataconnect-box {
+    width: 600px !important;
+    .ant-input-number-handler-wrap {
+        display: none;
+    }
+    .textarea-desc {
+        margin-top: 4px;
+    }
+    .validatemessage {
+        text-align: left;
+        color: #F5222D;
+    }
+}

+ 30 - 37
src/components/datasource/dataConnectConfig.jsx

@@ -2,68 +2,58 @@ import React from 'react'
 import { Layout, Form, Row, Col, Input, Icon, Menu, Dropdown, Divider, Upload, message, Card } from 'antd'
 import { connect } from 'dva'
 import DataConnectBox from './dataConnectBox'
-import '../../models/dataSource'
-import '../../models/dataConnect'
 const { Content } = Layout
 const CardGrid = Card.Grid
 const UploadDragger = Upload.Dragger
 const Search = Input.Search
 
 class DataConnectConfig extends React.Component {
-    constructor(props) {
-        super(props);
-        this.state = {
-            visibleBox: false,
-            operation: 'modify',
-            selected: null
-        };
-    }
     render() {
         const { dataSource, dataConnect, dispatch } = this.props;
-        const { visibleBox, operation, selected } = this.state;
+        const filterLabel = dataConnect.filterLabel;
 
         const generateCard = () => {
             const operationMenu = (
                 <Menu className='menu-operation'>
                     <Menu.Item onClick={() => {
-                        dispatch({ type: 'dataConnect/setNewModel', model: selected });
-                        this.setState({
-                            visibleBox: true,
-                            operation: 'modify'
-                        })
+                        dispatch({ type: 'dataConnect/setNewModel', model: dataConnect.selected });
+                        dispatch({ type: 'dataConnect/setNewModelFields', fields: [
+                            { name: 'visibleBox', value: true },
+                            { name: 'boxOperation', value: 'modify' }
+                        ] });
                     }}>
                         <Icon type='edit'/>编辑
                     </Menu.Item>
                     <Menu.Item onClick={(e) => {
-                        dispatch({ type: 'chart/remoteDelete', code: this.state.selectedCode });
+                        dispatch({ type: 'chart/remoteDelete', code: dataConnect.selected.code });
                     }}>
                         <Icon type='delete'/>删除
                     </Menu.Item>
                 </Menu>
             )
 
-            let cards = dataConnect.list.map( (l, i) => (
+            let cards = dataConnect.list.filter(l => {
+                return l.name.search(new RegExp('(' + filterLabel + '){1}', 'ig')) !== -1;
+            }).map( (l, i) => (
                 <CardGrid className='dataconnect-card' key={i}>
                     <Card
                         title={
                             <Row type='flex' justify='start'>
                                 <Col>{l.name}</Col>
-                                <div style={{ display: (selected && selected.code === l.code) ? 'block' : 'none' }} className='selected'></div>
+                                <div style={{ display: (dataConnect.selected && dataConnect.selected.code === l.code) ? 'block' : 'none' }} className='selected'></div>
                             </Row>
                         }
                         onClick={() => {
-                            this.setState({ selected: l }, () => {
-                                let s = this.state.selected;
-                                console.log(s);
-                                dispatch({ type: 'dataSource/setNewModelFields', fields: [
-                                    { name: 'dbType', value: s.dbType },
-                                    { name: 'address', value: s.address },
-                                    { name: 'port', value: s.port },
-                                    { name: 'dbName', value: s.dbName },
-                                    { name: 'userName', value: s.userName },
-                                    { name: 'password', value: s.password },
-                                ] });
-                            });
+                            // 选中项设置
+                            dispatch({ type: 'dataConnect/setSelected', selected: l });
+                            dispatch({ type: 'dataSource/setNewModelFields', fields: [
+                                { name: 'dbType', value: l.dbType },
+                                { name: 'address', value: l.address },
+                                { name: 'port', value: l.port },
+                                { name: 'dbName', value: l.dbName },
+                                { name: 'userName', value: l.userName },
+                                { name: 'password', value: l.password },
+                            ] });
                         }}
                     >
                         <div>
@@ -86,10 +76,10 @@ class DataConnectConfig extends React.Component {
             cards.unshift(
                 <CardGrid className='dataconnect-card dataconnect-card-create' key='create' onClick={() => {
                     dispatch({ type: 'dataConnect/setNewModel', model: {} });
-                    this.setState({
-                        operation: 'create',
-                        visibleBox: true
-                    })
+                    dispatch({ type: 'dataConnect/setNewModelFields', fields: [
+                        { name: 'visibleBox', value: true },
+                        { name: 'boxOperation', value: 'create' }
+                    ] });
                 }}>
                     <Card>
                         <Icon type="plus-circle-o" />
@@ -159,15 +149,18 @@ class DataConnectConfig extends React.Component {
                                     <Row type='flex' justify='end'>
                                         <Search
                                             style={{ width: '200px' }}
+                                            value={dataConnect.filterLabel}
                                             placeholder="请输入关键字"
-                                            onSearch={value => console.log(value)}
+                                            onChange={e => {
+                                                dispatch({ type: 'dataConnect/setFilterLabel', label: e.target.value });
+                                            }}
                                         />
                                     </Row>
                                 }>
                                     <div className='dataconnect-list'>
                                         { generateCard() }
                                     </div>
-                                    <DataConnectBox visibleBox={visibleBox} hideBox={() => {this.setState({visibleBox: false})}} operation={operation} />
+                                    <DataConnectBox />
                                 </Card>
                             </Content>
                         </Layout>

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

@@ -1,8 +1,6 @@
 import React from 'react'
-import { Layout, Row, Col, Input, Button, Table, Icon, Tag, Menu, Dropdown, Card } from 'antd'
+import { Layout, Row, Col, Input, Button, Table, Icon, Menu, Dropdown, Card } from 'antd'
 import { connect } from 'dva'
-import '../../models/dataSource'
-import '../../models/dataConnect'
 import './dataSource.less'
 import { dateFormat } from '../../utils/baseUtils'
 const { Content } = Layout
@@ -51,7 +49,7 @@ class DataSource extends React.Component {
     render() {
         
         const { dataSource, dispatch } = this.props;
-        const { list, loading, selectedRecord } = this.state;
+        const { loading, selectedRecord } = this.state;
         
         const moreOperatingMenu = (
             <Menu className='operationmenu'>
@@ -64,6 +62,7 @@ class DataSource extends React.Component {
                 </Menu.Item>
                 <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'}});
                     }}>

+ 19 - 8
src/components/datasource/dataSourceDetail.jsx

@@ -20,7 +20,7 @@ class DataSourceDetail extends React.Component {
             type: props.match.params.type,
             code: props.match.params.code,
             tab: props.match.params.tab,
-            current: ['base', 'column', 'access'].indexOf(props.match.params.tab)
+            current: ['base', 'column', 'access', 'other'].indexOf(props.match.params.tab)
         }
     }
 
@@ -34,23 +34,23 @@ class DataSourceDetail extends React.Component {
     next() {
         const { type, current } = this.state;
         this.setState({ current: current + 1 });
-        let step = ['base', 'column', 'access'][current + 1];
+        let step = ['base', 'column', 'access', 'other'][current + 1];
         this.props.dispatch({ type: 'main/redirect', path: '/datasource/' + type + '/create/' + step })
     }
 
     prev() {
         const { type, current } = this.state;
         this.setState({ current: current - 1 });
-        let step = ['base', 'column', 'access'][current - 1];
+        let step = ['base', 'column', 'access', 'other'][current - 1];
         this.props.dispatch({ type: 'main/redirect', path: '/datasource/' + type + '/create/' + step })
     }
 
     render() {
-        const { dispatch } = this.props;
+        const { dispatch, dataSource, dataConnect } = this.props;
         const { type, mode, code, tab, current } = this.state;
         const steps = [{
             tabName: 'base',
-            title: '数据连接配置',
+            title: type === 'database' ? '数据连接配置' : '文件选择',
             content: <DataConnectConfig mode={mode} />
         }, {
             tabName: 'column',
@@ -97,11 +97,18 @@ class DataSourceDetail extends React.Component {
                             }
                             {
                                 current < steps.length - 1
-                                && <Button type="primary" onClick={() => this.next()}>下一步</Button>
+                                && <Button disabled={
+                                    ( current === 0 && ( ( type === 'database' && !dataSource.newOne.address ) || ( type === 'file' && +1 === 2) ) ) ||
+                                    (current === 1 && (!dataSource.newOne.columns || dataSource.newOne.columns.length === 0))
+                                } type="primary" onClick={() => this.next()}>下一步</Button>
                             }
                             {
                                 current === steps.length - 1
-                                && <Button type="primary" onClick={() => {
+                                && <Button disabled={
+                                    !dataSource.newOne.name || 
+                                    !dataConnect.selected ||
+                                    (!dataSource.newOne.columns || dataSource.newOne.columns.length === 0)
+                                } type="primary" onClick={() => {
                                     dispatch({ type: 'dataSource/remoteAdd' })
                                 }}>完成</Button>
                             }
@@ -131,4 +138,8 @@ class DataSourceDetail extends React.Component {
     }
 }
 
-export default connect()(DataSourceDetail);
+function mapStateToProps({ present: { dataSource, dataConnect } }) {
+    return { dataSource, dataConnect }
+}
+
+export default connect(mapStateToProps)(DataSourceDetail);

+ 2 - 0
src/constants/url.js

@@ -29,6 +29,8 @@ const URLS = {
 
     DATACONNECT_LIST: BASE_URL + '/getDatabases', // 获得数据连接列表
 
+    DATACONNECT_VALIDATE: BASE_URL + '/testConnect', // 校验数据库连接是否合法
+
     /***************************************图表***************************************/
 
     CHART_ADD: BASE_URL + '/inputCharts', // 新增图表

+ 2 - 2
src/custom.less

@@ -1,5 +1,5 @@
-@import "/public/fonts/iconfont/custom/iconfont.css";
-@icon-url: "/public/fonts/iconfont/default"; // 把 iconfont 地址改到本地
+@import "/public/fonts/iconfont/custom/iconfont.css"; // 引入自定义的 iconfont
+@icon-url: "/public/fonts/iconfont/default"; // 把默认的 iconfont 地址改到本地
 
 :global(.anticon) {
     &:before {

+ 11 - 5
src/models/chart.js

@@ -5,7 +5,8 @@ import URLS from '../constants/url'
 export default {
     namespace: 'chart',
     state: {
-        list: []
+        list: [],
+        filterLabel: '',
     },
     reducers: {
         add(state, action) {
@@ -18,6 +19,9 @@ export default {
             let list = action.list;
             return Object.assign({}, state, {list: list});
         },
+        setFilterLabel(state, action) {
+            return Object.assign({}, state, {filterLabel: action.label});
+        }
     },
     effects: {
         *fetchList(action, { select, call, put }) {
@@ -36,7 +40,8 @@ export default {
                             name: d.chartName,
                             type: d.chartType,
                             creator: d.createBy,
-                            createTime: d.createDate
+                            createTime: d.createDate,
+                            description: d.describes
                         }
                     })
                     yield put({ type: 'list', list: list });
@@ -58,6 +63,7 @@ export default {
                     url: URLS.CHART_DETAIL,
                     body: code
                 });
+                console.log('解析图表数据', code, res);
                 if(!res.err && res.data.code > 0) {
                     const getViewType = function(type) {
                         if(type === 'Histogram') {
@@ -76,7 +82,7 @@ export default {
                     let resData = res.data.data;
                     let groupBy = JSON.parse(resData.groupBy) || [];
                     let chartConfig = JSON.parse(resData.chartConfig || '{ "xAxis": { "column": {}, "granularity": {} }, "yAxis": { "column": {}, "gauge": {} } }');
-                    let otherConfig = JSON.parse(resData.otherConfig || '{ "description": "" }');
+                    let otherConfig = JSON.parse(resData.otherConfig || '{}');
                     let viewType = getViewType(resData.chartType);
 
                     let data = {
@@ -96,7 +102,8 @@ export default {
                                 }
                             })[0]
                         },
-                        otherConfig: otherConfig
+                        otherConfig: otherConfig,
+                        description: resData.describes
                     }
 
                     if(viewType === 'bar') {
@@ -128,7 +135,6 @@ export default {
             }catch(e) {
                 console.log(e);
                 message.error('解析图表错误');
-                yield put({ type: 'list', data: [] });
             }
         },
         *remoteDelete(action, { select, call, put, takeEvery, takeLatest }) {

+ 21 - 11
src/models/chartDesigner.js

@@ -86,11 +86,12 @@ export default {
 
         },
         otherConfig:{
-            description: ''
         },
+        description: '',
         filters: [],
         chartOption: {},
-        autoRefresh: true
+        autoRefresh: true,
+        dirty: false
     },
     reducers: {
         /**
@@ -102,7 +103,7 @@ export default {
             let obj = {};
             obj[name] = value;
             let newState = Object.assign({}, state, obj);
-            return newState;
+            return Object.assign({}, newState, {dirty: true});
         },
         /**
          * 批量更新model字段值
@@ -113,7 +114,7 @@ export default {
             let obj = {};
             fields.map(f => (obj[f.name] = f.value));
             let newState = Object.assign({}, state, obj);
-            return newState;
+            return Object.assign({}, newState, {dirty: true});
         },
         /**
          * 更新model字段值
@@ -138,8 +139,14 @@ export default {
             return newState;
         },
         reset(state, action) {
-            let obj = Object.assign({}, state, state.originData);
-            return obj;
+            let newState = Object.assign({}, state, state.originData);
+            return Object.assign({}, newState, {dirty: false});
+        },
+        setDirty(state, action) {
+            const { dirty } = action;
+            let newState = Object.assign({}, state, { dirty });
+            console.log(newState);
+            return newState;
         }
     },
     effects: {
@@ -231,7 +238,7 @@ export default {
         *remoteAdd(action, { select, call, put }) {
             try{
                 const chartDesigner = yield select(state => state.present.chartDesigner);
-                const { header, baseConfig, preparing, barConfig, pieConfig, lineConfig, otherConfig } = chartDesigner;
+                const { header, baseConfig, preparing, barConfig, pieConfig, lineConfig, otherConfig, description } = chartDesigner;
 
                 let body = {
                     chartName: header.label,
@@ -241,7 +248,7 @@ export default {
                         columnRamane: preparing.groupBy.label
                     }] : [],
                     createBy: 'zhuth',
-                    describes: '',
+                    describes: description,
                     style: '',
                     otherConfig: JSON.stringify(otherConfig)
                 }; // 基本属性
@@ -274,7 +281,8 @@ export default {
         *remoteModify(action, { select, call, put }) {
             try{
                 const chartDesigner = yield select(state => state.present.chartDesigner);
-                const { code, header, baseConfig, pieConfig, lineConfig, preparing, barConfig, scatterConfig, otherConfig } = chartDesigner;
+                const { code, header, baseConfig, pieConfig, lineConfig, preparing,
+                    barConfig, scatterConfig, otherConfig, description } = chartDesigner;
                 let body = {
                     chartId: code,
                     chartName: header.label,
@@ -284,7 +292,7 @@ export default {
                         columnRamane: preparing.groupBy.label
                     }] : [],
                     createBy: 'zhuth',
-                    describes: '',
+                    describes: description,
                     style: '',
                     otherConfig: JSON.stringify(otherConfig)
                 }; // 基本属性
@@ -301,12 +309,13 @@ export default {
                     body.chartType = 'scatter';
                     body.chartConfig = JSON.stringify(scatterConfig);
                 }
+                console.log(body);
                 const res = yield call(service.fetch, {
                     url: URLS.CHART_UPDATE,
                     body: body
                 })
                 if(!res.err && res.data.code > 0) {
-                    message.success('修改成功!');
+                    message.success('修改成功');
                     yield put({ type: 'chart/fetchList', mandatory: true });
                 }else {
                     message.error('修改失败');
@@ -323,6 +332,7 @@ export default {
                     url: URLS.DATASOURCE_QUERY_DATACOLUMNS,
                     body: code
                 });
+                console.log(code, res);
                 if(!res.err && res.data.code > 0) {
                     let resData = res.data.data;
 

+ 126 - 38
src/models/dataConnect.js

@@ -1,12 +1,15 @@
 import { message } from 'antd'
 import * as service from '../services/index'
 import URLS from '../constants/url'
+import RULE from './validRule.js'
 
 export default {
     namespace: 'dataConnect',
     state: {
         list: [],
-        newOne: {}
+        newOne: {},
+        filterLabel: '',
+        validInfo: {}
     },
     reducers: {
         list(state, action) {
@@ -43,10 +46,32 @@ export default {
             return Object.assign({}, state, {list});
 
         },
+        setSelected(state, action) {
+            const { selected } = action;
+            return Object.assign({}, state, {selected});
+        },
+        setFilterLabel(state, action) {
+            const { label } = action;
+            return Object.assign({}, state, { filterLabel: label });
+        },
         setNewModelField(state, action) {
-            const { name, value } = action;
+            const { name, value, rules } = action;
             let newOne = state.newOne;
+            let validInfo = state.validInfo;
             newOne[name] = value;
+            newOne.invalid = false;
+            (rules && (Object.prototype.toString.call(rules) === '[object Array]')) && rules.map((r) => {
+                return validInfo[name] = RULE[r](value);
+            });
+            
+            return Object.assign({}, state, { newOne, validInfo });
+        },
+        setNewModelFields(state, action) {
+            const { fields } = action;
+            let newOne = state.newOne;
+            for(let i = 0; i < fields.length; i++) {
+                newOne[fields[i]['name']] = fields[i]['value'];
+            }
             return Object.assign({}, state, {newOne});
         },
         setNewModel(state, action) {
@@ -56,6 +81,12 @@ export default {
         },
         resetNewModel(state, action) {
             return Object.assign({}, state, {newOne: {}});
+        },
+        setNewModelInvalid(state, action) {
+            const { value } = action;
+            let newOne = state.newOne;
+            newOne.invalid = value;
+            return Object.assign({}, state, {newOne});
         }
     },
     effects: {
@@ -97,24 +128,58 @@ export default {
             const dataConnect = yield select(state => state.present.dataConnect);
             const model = dataConnect.newOne;
             try {
-                yield call(service.fetch, {
-                    url: URLS.DATACONNECT_ADD,
-                    body: {
-                        "name": model.name,
-                        "addrass": model.address,
-                        "port": model.port,
-                        "databaseType": model.dbType,
-                        "dataName": model.dbName,
-                        "userName": model.userName,
-                        "passWord": model.password,
-                        "note": model.description
-                    }
+                // 设置validating为true
+                yield put({ type: 'setNewModelField', name: 'validating', value: true });
+                // 调用检测接口检测连接配置是否合法
+                let validBody = {
+                    name: model.name,
+                    addrass: model.address,
+                    port: model.port,
+                    databaseType: model.dbType,
+                    dataName: model.dbName,
+                    userName: model.userName,
+                    passWord: model.password
+                }
+                const validRes = yield call(service.fetch, {
+                    url: URLS.DATACONNECT_VALIDATE,
+                    body: validBody
                 });
-                yield put({ type: 'add' });
-                message.success('新增成功');
+
+                // 设置validating为false
+                yield put({ type: 'setNewModelField', name: 'validating', value: false });
+
+                if(!validRes.err && validRes.data.code > 0) {
+                    // 如果合法
+                    // 设置valid为true
+                    yield put({ type: 'setNewModelInvalid', name: 'invalid', value: false });
+                    // 继续执行保存
+                    let body = {
+                        name: model.name,
+                        addrass: model.address,
+                        port: model.port,
+                        databaseType: model.dbType,
+                        dataName: model.dbName,
+                        userName: model.userName,
+                        passWord: model.password,
+                        note: model.description
+                    }
+                    const res = yield call(service.fetch, {
+                        url: URLS.DATACONNECT_ADD,
+                        body: body
+                    });
+                    if(!res.err && res.data.code > 0) {
+                        yield put({ type: 'add' });
+                        yield put({ type: 'setNewModelField', name: 'visibleBox', value: false });
+                        message.success('新增成功');
+                    }else {
+                        message.error('新增失败');
+                    }
+                }else {
+                    yield put({ type: 'setNewModelInvalid', name: 'invalid', value: true });
+                }
+                
             }catch(e) {
-                message.error('新增失败');
-                //yield takeLatest({ type: 'loadList', data: [] });
+                console.log(e);
             }
         },
         *remoteModify(action, { select, call, put }) {
@@ -124,34 +189,57 @@ export default {
                 const code = action.code;
                 let list = dataConnect.list;
     
-    
-                let data = {
-                    id: code,
+                // 设置validating为true
+                yield put({ type: 'setNewModelField', name: 'validating', value: true });
+                // 调用检测接口检测连接配置是否合法
+                let validBody = {
                     name: model.name,
                     addrass: model.address,
                     port: model.port,
                     databaseType: model.dbType,
                     dataName: model.dbName,
                     userName: model.userName,
-                    passWord: model.password,
-                    note: model.description
-                };
-                const res = yield call(service.fetch, {
-                    url: URLS.DATACONNECT_UPDATE,
-                    body: data
+                    passWord: model.password
+                }
+                const validRes = yield call(service.fetch, {
+                    url: URLS.DATACONNECT_VALIDATE,
+                    body: validBody
                 });
-                if(!res.err && res.data.code > 0) {
-                    list = list.map(l => {
-                        if(l.code === action.code) {
-                            l = model;
-                        }
-                        return l;
+
+                // 设置validating为false
+                yield put({ type: 'setNewModelField', name: 'validating', value: false });
+
+                if(!validRes.err && validRes.data.code > 0) {
+                    let data = {
+                        id: code,
+                        name: model.name,
+                        addrass: model.address,
+                        port: model.port,
+                        databaseType: model.dbType,
+                        dataName: model.dbName,
+                        userName: model.userName,
+                        passWord: model.password,
+                        note: model.description
+                    };
+                    const res = yield call(service.fetch, {
+                        url: URLS.DATACONNECT_UPDATE,
+                        body: data
                     });
-                    yield put({ type: 'list', data: list });
-                    message.success('修改成功');
+                    if(!res.err && res.data.code > 0) {
+                        list = list.map(l => {
+                            if((l.code+'') === (action.code+'')) {
+                                l = model;
+                            }
+                            return l;
+                        });
+                        yield put({ type: 'list', data: list });
+                        message.success('修改成功');
+                        yield put({ type: 'setNewModelField', name: 'visibleBox', value: false });
+                    }else {
+                        message.error('修改失败');
+                    }
                 }else {
-                    message.error('修改失败');
-    
+                    yield put({ type: 'setNewModelInvalid', name: 'invalid', value: true });
                 }
             }catch(e) {
                 message.error('修改失败');
@@ -168,7 +256,7 @@ export default {
                 });
                 if(!res.err && res.data.code > 0) {
                     for(let i = 0; i < list.length; i++) {
-                        if(list[i].code === code) {
+                        if((list[i].code+'') === (code+'')) {
                             list.splice(i, 1);
                             break;
                         }

+ 47 - 42
src/models/dataSource.js

@@ -1,6 +1,7 @@
 import { message } from 'antd'
 import * as service from '../services/index'
 import URLS from '../constants/url'
+import DEFAULT_COLUMN_TYPE from './defaultColumnType.json'
 
 export default {
     namespace: 'dataSource',
@@ -148,9 +149,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 });
+                    // let list = dataSource.list;
+                    // model.code = res.data.data;
+                    // list.unshift(model);
+                    yield put({ type: 'fetchList', mandatory: true });
                     yield put({ type: 'main/redirect', path: { pathname: '/datasource' } });
                     message.success('新增成功!');
                 }else {
@@ -162,12 +164,18 @@ export default {
             }
         },
         *remoteDetail(action, { select, call, put }) {
-            const code = action.code;
             try {
+                const code = action.code;
+                const dataSource = yield select(state => state.present.dataSource);
+                const model = dataSource.newOne;
+                if((code + '') === (model.code + '')) {
+                    return;
+                }
                 const res = yield call(service.fetch, {
                     url: URLS.DATASOURCE_DETAIL,
                     body: code
                 });
+                console.log('dataSource/remoteDetail', code, res);
                 if(!res.err && res.data.code > 0) {
                     let resData = res.data.data;
                     let columnConfig = JSON.parse(resData.columnConfig);
@@ -214,47 +222,44 @@ export default {
             }
         },
         *importNewModelColumns(action, { select, call, put }) {
-            const dataSource = yield select(state => state.present.dataSource);
-            const sqlStr = dataSource.newOne.target;
-            const res = yield call(service.fetch, {
-                url: URLS.DATASOURCE_QUERY_SQLCOLUMNS,
-                body: {
-                    "String": sqlStr
-                }
-            });
-
-            const getColumnType = (dataType) => {
-                let columnType = 'string';
-                if(dataType === 'VARCHAR2') {
-                    columnType = 'categorical';
-                }else if(dataType === 'NUMBER') {
-                    columnType = 'scale'
-                }else if(dataType === 'DATE') {
-                    columnType = 'time'
-                }else if(dataType === 'NUMBER') {
-                    columnType = 'scale'
-                }
-                return columnType;
-            }
-
-            if(!res.err && res.data.code > 0) {
-                let columns = res.data.data.map((d, i) => {
-                    return {
-                        key: i,
-                        using: true,
-                        name: d.columnName,
-                        alias: d.remarks?d.remarks.substring(0, 10):'',
-                        dataType: d.columnType,
-                        columnType: getColumnType(d.columnType),
-                        groupable: d.columnType === 'VARCHAR2',
-                        bucketizable: d.columnType === 'NUMBER',
-                        description: d.remarks
+            try{
+                const dataSource = yield select(state => state.present.dataSource);
+                const sqlStr = dataSource.newOne.target;
+                const res = yield call(service.fetch, {
+                    url: URLS.DATASOURCE_QUERY_SQLCOLUMNS,
+                    body: {
+                        baseName: dataSource.newOne.userName,
+                        strSql: sqlStr
                     }
                 });
-                yield put({ type: 'setNewModelField', name: 'columns', value: columns });
-            }else {
+    
+                const getColumnType = (dataType) => {
+                    return DEFAULT_COLUMN_TYPE[dataType] || 'string';
+                }
+    
+                if(!res.err && res.data.code > 0) {
+                    let columns = res.data.data.map((d, i) => {
+                        return {
+                            key: i,
+                            using: true,
+                            name: d.columnName,
+                            alias: d.remarks?d.remarks.substring(0, 10):'',
+                            dataType: d.columnType,
+                            columnType: getColumnType(d.columnType),
+                            groupable: d.columnType === 'VARCHAR2',
+                            bucketizable: d.columnType === 'NUMBER',
+                            description: d.remarks
+                        }
+                    });
+                    yield put({ type: 'setNewModelField', name: 'columns', value: columns });
+                }else {
+                    yield put({ type: 'setNewModelField', name: 'columns', value: [] });
+                    message.error('请求列数据错误');
+                }
+            }catch(e) {
+                console.log(e);
                 yield put({ type: 'setNewModelField', name: 'columns', value: [] });
-                message.error('请求数据错误');
+                message.error('请求数据错误');
             }
         },
         *remoteDelete(action, { select, call, put }) {

+ 11 - 0
src/models/defaultColumnType.json

@@ -0,0 +1,11 @@
+{
+    "VARCHAR": "categorical",
+    "VARCHAR2": "categorical",
+    "DATE": "time",
+    "NUMBER": "scale",
+    "CLOB": "string",
+    "FLOAT": "scale",
+    "INT": "scale",
+    "INTEGER": "scale",
+    "LONG": "index"
+}

+ 45 - 0
src/models/validRule.js

@@ -0,0 +1,45 @@
+const STATE = {
+    VALIDATING: 'validating', // 校验中
+    SUCCESS: 'success', // 校验通过
+    ERROR: 'error', // 校验不通过
+    WARNING: 'warning' // 存在问题但通过校验
+}
+const RULE = {
+    // 默认校验信息
+    validInfo: {
+        validateStatus: STATE.SUCCESS,
+        help: ''
+    },
+    // 必填
+    required: function(value) {
+        if(!value) {
+            this.validInfo.validateStatus = STATE.ERROR;
+            this.validInfo.help = '不能为空';
+        }else {
+            this.validInfo.validateStatus = STATE.SUCCESS
+        }
+        return this.validInfo;
+    },
+    // 长度
+    length: function(value, maxLen, minLen) {
+        let str = value + '';
+        if(maxLen < minLen) {
+            return this.validInfo;
+        }
+        if(maxLen > 0) {
+            if(str.length > maxLen) {
+                this.validInfo.validateStatus = STATE.ERROR;
+                this.validInfo.help = '长度不能大于' + maxLen;
+            }
+        }
+        if(minLen >= 0) {
+            if(str.length < minLen) {
+                this.validInfo.validateStatus = STATE.ERROR;
+                this.validInfo.help = '长度不能小于' + minLen;
+            }
+        }
+        return this.validInfo;
+    },
+};
+
+export default RULE;

+ 1 - 10
src/utils/baseUtils.js

@@ -98,15 +98,6 @@ function isEqual(a,b){
     }
 }
 
-function isEmptyObject(model) {
-    var isEmpty = true;
-    for (var i = 0; i < model.length; i++) {
-        isEmpty = false;
-        break;
-    }
-    return isEmpty;
-}
-
 function getUrlParam(name) {   
     var reg = new RegExp("(^|&)"+name+"=([^&]*)(&|$)");   
     var r = window.location.search.substr(1).match(reg);   
@@ -133,7 +124,7 @@ function delay(timeout) {
     });
 }
 
-export { remove, isEqual, isEmptyObject, getUrlParam, hashcode, delay, dateFormat };
+export { remove, isEqual, getUrlParam, hashcode, delay, dateFormat };
 
 function dateFormat(date, fmt) {
     date = new Date(date);