zhuth 7 жил өмнө
parent
commit
69bd475e1b

+ 1 - 1
app/components/chartDesigner/sections/filterBox.jsx

@@ -288,7 +288,7 @@ class FilterBox extends React.Component {
         return (
             <Modal
                 className='filter-box'
-                title="筛选条件"
+                title={<div>筛选条件<div className='clear' onClick={()=>{this.removeAllFilters()}}>清空条件<Icon type='delete'/></div></div>}
                 visible={visibleFilterBox}
                 onOk={this.getFilters}
                 onCancel={hideFilterBox}

+ 6 - 0
app/components/chartDesigner/sections/filterBox.less

@@ -23,4 +23,10 @@
 }
 .filter-remove-col {
   text-align: center;
+}
+.clear {
+  float: right;
+  margin-right: 32px;
+  cursor: pointer;
+  color: red;
 }

+ 5 - 5
app/components/common/navigator.jsx

@@ -19,14 +19,14 @@ class Navigator extends React.Component {
             <Menu.Item key="mypage">
                 <Link to='/home'><Icon type="home" />我的</Link>
             </Menu.Item>
-            <Menu.Item key="datasource">
-                <Link to='/dataSource'><Icon type="database" />数据源</Link>
+            <Menu.Item key="dashboards">
+                <Icon type="desktop" />看板与报告
             </Menu.Item>
             <Menu.Item key="charts">
-                <Link to='/chartDesigner'><Icon type="area-chart" />图表</Link>
+                <Link to='/chartdesigner'><Icon type="area-chart" />图表</Link>
             </Menu.Item>
-            <Menu.Item key="dashboards">
-                <Icon type="desktop" />看板与报告
+            <Menu.Item key="datasource">
+                <Link to='/datasource'><Icon type="database" />数据源</Link>
             </Menu.Item>
             <Menu.Item key="modeling">
                 <Icon type="tool" />建模分析

+ 153 - 96
app/components/datasource/baseConfig.jsx

@@ -1,12 +1,13 @@
 import React from 'react'
-import { Button, Form, Row, Col, Input, InputNumber, Select, Icon, Menu, Dropdown, Divider } from 'antd'
+import { Button, Form, Row, Col, Input, InputNumber, Select, Icon, Menu, Dropdown, Divider, Upload, message } from 'antd'
 const FormItem = Form.Item
 const SelectOption = Select.Option
 const OptionGroup = Select.OptGroup
 const InputGroup = Input.Group
 const SubMenu = Menu.SubMenu
 const MenuItem = Menu.Item
-const MenuItemGroup = Menu.ItemGroup;
+const MenuItemGroup = Menu.ItemGroup
+const UploadDragger = Upload.Dragger
 import { connect } from 'dva'
 import dataSource from '../../models/dataSource'
 
@@ -48,102 +49,158 @@ const DataSourceBaseConfig = ({ dataSource, dispatch }) => {
                     onChange={(e) => { dispatch({ type: 'dataSource/setNewModelField', name: 'name', value: e.target.value }) }}>
                 </Input>
             </FormItem>
-            <Divider orientation="left">连接配置</Divider>
-            <div className='links'>
-                <Dropdown trigger={['click']} overlay={dataSourceLinkMenu}>
-                    <div>使用已存在的数据连接 <Icon type="down" /></div>
-                </Dropdown>
-            </div>
-            <FormItem label='数据库类型' {...formItemLayout}>
-                <Select
-                    value={dataSource.newOne.type}
-                    labelInValue={true}
-                    onChange={(value) => {
-                        dispatch({ type: 'dataSource/setNewModelField', name: 'type', 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
-                            value={dataSource.newOne.address}
-                            onChange={(e) => {
-                                dispatch({ type: 'dataSource/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={dataSource.newOne.port}
-                            onChange={(value) => {
-                                dispatch({ type: 'dataSource/setNewModelField', name: 'port', value: value });
-                            }}
-                        />
-                    </FormItem>
-                </Col>
-            </Row>
-            <FormItem label='数据库名' {...formItemLayout}>
-                <Input
-                    value={dataSource.newOne.dbName}
-                    onChange={(e) => {
-                        dispatch({ type: 'dataSource/setNewModelField', name: 'dbName', value: e.target.value });
-                    }}
-                />
-            </FormItem>
-            <Row>
-                <Col span={12}>
-                    <FormItem label='用户名' {...{
-                        labelCol: { span: 8 },
-                        wrapperCol: { span: 16 }
-                    }}>
-                        <Input
-                            value={dataSource.newOne.userName}
-                            onChange={(e) => {
-                                dispatch({ type: 'dataSource/setNewModelField', name: 'userName', value: e.target.value });
+            {
+                dataSource.newOne.type=='file'?(
+                    <div>
+                        <Divider orientation="left">选择文件</Divider>
+                        <UploadDragger
+                            className='upload'
+                            name='file' // 上传到后台的文件名
+                            fileList={dataSource.newOne.fileList}
+                            accept='application/vnd.ms-excel,.csv' // 只支持excel和csv格式文件
+                            action='//jsonplaceholder.typicode.com/posts/'
+                            headers={{authorization: 'authorization-text'}}
+                            beforeUpload={(file, fileList) => {
+                                const trueType = file.type === 'application/vnd.ms-excel';
+                                if (!trueType) {
+                                    message.error('选择文件格式错误!');
+                                }
+                                const trueSize = file.size / 1024 / 1024 < 30;
+                                if (!trueSize) {
+                                    message.error('选择文件过大!');
+                                }
+                                return trueType && trueSize;
                             }}
-                        />
-                    </FormItem>
-                </Col>
-                <Col span={12}>
-                    <FormItem label='密码' {...{
-                        labelCol: { span: 8 },
-                        wrapperCol: { span: 16 }
-                    }}>
-                        <Input
-                            type='password'
-                            value={dataSource.newOne.password}
-                            onChange={(e) => {
-                                dispatch({ type: 'dataSource/setNewModelField', name: 'password', value: e.target.value });
+                            onChange={(info) => {
+                                const file = info.file;
+                                let fileList = info.fileList;
+                                fileList = fileList.slice(-1); // 只保留最后一个
+                                if (info.file.status !== 'uploading') {
+                                    console.log(file, info.fileList);
+                                    const trueType = file.type === 'application/vnd.ms-excel';
+                                    const trueSize = file.size / 1024 / 1024 < 30;
+                                    if(!trueType || !trueSize) {
+                                        fileList = [];
+                                    }
+                                }
+                                if (file.status === 'done') {
+                                    message.success(`${file.name} 文件上传成功`);
+                                } else if (file.status === 'error') {
+                                    fileList = [];
+                                    message.error(`${file.name} 文件上传失败.`);
+                                }
+                                dispatch({ type: 'dataSource/setNewModelField', name: 'fileList', value: fileList })
                             }}
-                        />
-                    </FormItem>
-                </Col>
-            </Row>
-            <div className='buttons'>
-                <Button size='small'>测试连接</Button>
-            </div>
+                        >
+                            <p className="ant-upload-drag-icon">
+                                <Icon type="inbox" />
+                            </p>
+                            <p className="ant-upload-text">点击选择或拖动文件到该区域</p>
+                            <p className="ant-upload-hint">仅支持上传单个30MB以内EXCEL/CSV格式文件</p>
+                        </UploadDragger>
+                    </div>
+                ):(
+                    <div>
+                        <Divider orientation="left">连接配置</Divider>
+                        <div className='links'>
+                            <Dropdown trigger={['click']} overlay={dataSourceLinkMenu}>
+                                <div>使用已存在的数据连接 <Icon type="down" /></div>
+                            </Dropdown>
+                        </div>
+                        <FormItem label='数据库类型' {...formItemLayout}>
+                            <Select
+                                value={dataSource.newOne.dbType}
+                                labelInValue={true}
+                                onChange={(value) => {
+                                    dispatch({ type: 'dataSource/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
+                                        value={dataSource.newOne.address}
+                                        onChange={(e) => {
+                                            dispatch({ type: 'dataSource/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={dataSource.newOne.port}
+                                        onChange={(value) => {
+                                            dispatch({ type: 'dataSource/setNewModelField', name: 'port', value: value });
+                                        }}
+                                    />
+                                </FormItem>
+                            </Col>
+                        </Row>
+                        <FormItem label='数据库名' {...formItemLayout}>
+                            <Input
+                                value={dataSource.newOne.dbName}
+                                onChange={(e) => {
+                                    dispatch({ type: 'dataSource/setNewModelField', name: 'dbName', value: e.target.value });
+                                }}
+                            />
+                        </FormItem>
+                        <Row>
+                            <Col span={12}>
+                                <FormItem label='用户名' {...{
+                                    labelCol: { span: 8 },
+                                    wrapperCol: { span: 16 }
+                                }}>
+                                    <Input
+                                        value={dataSource.newOne.userName}
+                                        onChange={(e) => {
+                                            dispatch({ type: 'dataSource/setNewModelField', name: 'userName', value: e.target.value });
+                                        }}
+                                    />
+                                </FormItem>
+                            </Col>
+                            <Col span={12}>
+                                <FormItem label='密码' {...{
+                                    labelCol: { span: 8 },
+                                    wrapperCol: { span: 16 }
+                                }}>
+                                    <Input
+                                        value={dataSource.newOne.password}
+                                        onChange={(e) => {
+                                            let value = e.target.value;
+                                            dispatch({ type: 'dataSource/setNewModelField', name: 'password', value: value });
+                                            e.target.removeAttribute('value')
+                                        }}
+                                    />
+                                </FormItem>
+                            </Col>
+                        </Row>
+                        <div className='buttons'>
+                            <Button size='small'>测试连接</Button>
+                        </div>
+                    </div>
+                )
+            }
             <Divider orientation="left">其他配置</Divider>
             <FormItem label='标签' {...formItemLayout}>
                 <Select

+ 289 - 101
app/components/datasource/columnConfig.jsx

@@ -10,112 +10,300 @@ const MenuItemGroup = Menu.ItemGroup;
 import { connect } from 'dva'
 import dataSource from '../../models/dataSource'
 
-const DataSourceColumnConfig = ({ dataSource, dispatch }) => {
-
-    const formItemLayout = {
-        labelCol: { span: 4 },
-        wrapperCol: { span: 20 },
-    };
-
-    const dataSourceLinkMenu = (
-        <Menu
-            className='menu-datasource-link'
-            onClick={() => {
-                dispatch({ type: 'dataSource/setNewModelField', name: 'address', value: '1111adddd' });
-                dispatch({ type: 'dataSource/setNewModelField', name: 'port', value: '1234' });
-                dispatch({ type: 'dataSource/setNewModelField', name: 'type', value: {
-                    key: 'oracle',
-                    label: 'ORACLE'
-                } });
-                dispatch({ type: 'dataSource/setNewModelField', name: 'dbName', value: 'orcl' });
-                dispatch({ type: 'dataSource/setNewModelField', name: 'userName', value: 'UAS' });
-                dispatch({ type: 'dataSource/setNewModelField', name: 'password', value: 'select!#%*(' });
-            }}
-        >
-            <MenuItem>1111</MenuItem>
-            <MenuItem>2222</MenuItem>
-            <MenuItem>33333</MenuItem>
-            <MenuItem>44</MenuItem>
-        </Menu>
-    );
-
-    const columns = [{
-        title: <div><Checkbox
-            style={{ margin: '0 8px 0 0' }}
-            indeterminate={dataSource.newOne.columns?(dataSource.newOne.columns.filter(c=>c.using).length>0&&dataSource.newOne.columns.filter(c=>c.using).length<(dataSource.newOne.columns.length)):false}
-            indeterminate={false}
-        />启用</div>,
-        dataIndex: 'using',
-        key: 'using',
-        render: () => <Checkbox
+class DataSourceColumnConfig extends React.Component {
+
+    constructor(props) {
+        super(props);
+        this.state = {
             
-        />
-    }, {
-        title: '列名',
-        dataIndex: 'name',
-        key: 'name',
-    }, {
-        title: '别名',
-        dataIndex: 'alias',
-        key: 'alias',
-    }, {
-        title: '类型',
-        dataIndex: 'type',
-        key: 'type',
-    }, {
-        title: '允许分组',
-        dataIndex: 'groupable',
-        key: 'groupable',
-        render: () => <Switch />
-    }, {
-        title: '允许分段',
-        dataIndex: 'bucketizable',
-        key: 'bucketizable',
-        render: () => <Switch />
-    }, {
-        title: '备注',
-        dataIndex: 'description',
-        key: 'description'
-    }];
-
-    return (
-        <div>
-            <Form size='small'>
-                <Divider orientation="left">数据对象</Divider>
-                <FormItem className='textarea-target'>
-                    <Input.TextArea
-                        placeholder='输入表名或查询SQL'
-                        autosize={{ minRows: 3 }}
-                    />
-                </FormItem>
-                <div className='buttons'>
-                    <Button onClick={() => {
-                        dispatch({ type: 'dataSource/setNewModelField', name: 'columns', value: [
-                            { key: 0, name: 'rrrrrr', using: false },
-                            { key: 1, name: 'ttttt', using: true },
-                            { key: 2, name: 'ssssss', using: true },
-                            { key: 3, name: 'yyyyyy', using: true },
-                            { key: 4, name: 'uuuuuuu', using: true },
-                            { key: 5, name: 'ggggg', using: true }
-                        ] });
-                    }}>连接</Button>
-                </div>
-            </Form>
-            <Divider orientation="left">结果集数据列</Divider>
-            <Table
-                className='table-columnconfig'
-                dataSource={dataSource.newOne.columns}
-                columns={columns}
-                locale={{
-                    emptyText: '未连接到数据对象'
+        }
+    }
+
+    onCheckAllChange() {
+
+    }
+
+    render() {
+        const { dataSource, dispatch } = this.props;
+
+        const columns = [{
+            title: <div><Checkbox
+                style={{ margin: '0 8px 0 0', display: dataSource.newOne.columns ? (dataSource.newOne.columns.length > 0 ? 'inline-block' : 'none') : 'none'}}
+                indeterminate={dataSource.newOne.columns ? (dataSource.newOne.columns.filter(c => c.using).length > 0 && dataSource.newOne.columns.filter(c => c.using).length < dataSource.newOne.columns.length) : false}
+                checked={dataSource.newOne.columns ? (dataSource.newOne.columns.filter(c => c.using).length == dataSource.newOne.columns.length) : false}
+                onChange={(e) => {
+                    let target = e.target;
+                    let columns = dataSource.newOne.columns ? dataSource.newOne.columns.map(c => {
+                        c.using = target.checked;
+                        return c;
+                    }) : [];
+
+                    dispatch({ type: 'dataSource/setNewModelField', name: 'columns', value: columns });
+                }}
+            />启用</div>,
+            dataIndex: 'using',
+            key: 'using',
+            width: 100,
+            render: (v, r) => <Checkbox
+                dataKey={r.key}
+                onChange={(e) => {
+                    let target = e.target;
+                    let key = target.dataKey;
+                    let columns = dataSource.newOne.columns.map(c => {
+                        if(c.key == key) {
+                            c.using = target.checked;
+                        }
+                        return c;
+                    });
+
+                    dispatch({ type: 'dataSource/setNewModelField', name: 'columns', value: columns });
                 }}
+                checked={v}
             />
-        </div>
-    );
+        }, {
+            title: '列名',
+            dataIndex: 'name',
+            key: 'name',
+            width: 180
+        }, {
+            title: '别名',
+            dataIndex: 'alias',
+            key: 'alias',
+            width: 180,
+            render: (text, record) => {
+                return (<EditableCell
+                    dispatch={dispatch}
+                    dataSource={dataSource}
+                    fieldName='alias'
+                    dataKey={record.key}
+                    type='input'
+                    value={text}
+                />)
+            }
+        }, {
+            title: '类型',
+            dataIndex: 'type',
+            key: 'type',
+            width: 120,
+            render: (text, record) => {
+                return (
+                    <Select
+                        style={{ width: '100%' }}
+                        value={text}
+                        onChange={(value) => {
+                            let columns = dataSource.newOne.columns.map(c => {
+                                if(c.key == record.key) {
+                                    c.type = value;
+                                    c.groupable = c.type == 'categorical';
+                                    c.bucketizable = ['time', 'scale', 'ordinal'].indexOf(record.type) != -1;
+                                }
+                                return c;
+                            });
+                            dispatch({ type: 'dataSource/setNewModelField', name: 'columns', value: columns });
+                        }}
+                    >
+                        <SelectOption value='index'>索引</SelectOption>
+                        <SelectOption value='time'>时间</SelectOption>
+                        <SelectOption value='categorical'>类别</SelectOption>
+                        <SelectOption value='scale'>标量</SelectOption>
+                        <SelectOption value='ordinal'>序值</SelectOption>
+                        <SelectOption value='string'>字符串</SelectOption>
+                    </Select>
+                )
+            }
+        }, {
+            title: '允许分组',
+            dataIndex: 'groupable',
+            key: 'groupable',
+            width: 100,
+            render: (value, record) => <Switch disabled={record.type!='categorical'} checked={value} onChange={(checked) => {
+                let columns = dataSource.newOne.columns.map(c => {
+                    if(c.key == record.key) {
+                        c.groupable = checked;
+                    }
+                    return c;
+                });
+                dispatch({ type: 'dataSource/setNewModelField', name: 'columns', value: columns });
+            }}/>
+        }, {
+            title: '允许分段',
+            dataIndex: 'bucketizable',
+            key: 'bucketizable',
+            width: 100,
+            render: (value, record) => <Switch disabled={['time', 'scale', 'ordinal'].indexOf(record.type)==-1} checked={value} onChange={(checked) => {
+                let columns = dataSource.newOne.columns.map(c => {
+                    if(c.key == record.key) {
+                        c.bucketizable = checked;
+                    }
+                    return c;
+                });
+                dispatch({ type: 'dataSource/setNewModelField', name: 'columns', value: columns });
+            }}/>
+        }, {
+            title: '备注',
+            dataIndex: 'description',
+            key: 'description',
+            render: (text, record) => {
+                return (<EditableCell
+                    dispatch={dispatch}
+                    dataSource={dataSource}
+                    fieldName='description'
+                    dataKey={record.key}
+                    type='input'
+                    value={text}
+                />)
+            }
+        }];
+
+        return (
+            <div>
+                {
+                    dataSource.newOne.type=='database'?(
+                        <div>
+                            <Form size='small'>
+                                <Divider orientation="left">数据对象</Divider>
+                                <FormItem className='textarea-target'>
+                                    <Input.TextArea
+                                        placeholder='输入表名或查询SQL'
+                                        autosize={{ minRows: 3 }}
+                                        value={dataSource.newOne.target}
+                                        onChange={(e) => {
+                                            dispatch({ type: 'dataSource/setNewModelField', name: 'target', value: e.target.value });
+                                        }}
+                                    />
+                                </FormItem>
+                                <div className='buttons'>
+                                    <Button onClick={() => {
+                                        dispatch({
+                                            type: 'dataSource/setNewModelField', name: 'columns', value: [
+                                                { key: 30, name: 'rrrrrr', type: 'ordinal', using: false},
+                                                { key: 40, name: 'rrrrrr', type: 'ordinal', using: false},
+                                                { key: 50, name: 'rrrrrr', type: 'ordinal', using: false},
+                                                { key: 60, name: 'rrrrrr', type: 'ordinal', using: false},
+                                                { key: 70, name: 'rrrrrr', type: 'ordinal', using: false},
+                                                { key: 80, name: 'rrrrrr', type: 'ordinal', using: false},
+                                                { key: 120, name: 'rrrrrr', type: 'ordinal', using: false},
+                                                { key: 64, name: 'rrrrrr', type: 'ordinal', using: false},
+                                                { key: 12, name: 'rrrrrr', type: 'ordinal', using: false},
+                                                { key: 98, name: 'rrrrrr', type: 'ordinal', using: false},
+                                                { key: 630, name: 'rrrrrr', type: 'ordinal', using: false},
+                                                { key: 1, name: 'ttttt', type: 'scale', using: true},
+                                                { key: 2, name: 'ssssss', type: 'index', using: true },
+                                                { key: 3, name: 'yyyyyy', type: 'categorical', using: true, description: 'hhhhhhhhh' },
+                                                { key: 4, name: 'uuuuuuu', type: 'string', using: true },
+                                                { key: 5, name: 'ggggg', type: 'time', alias: 'gg', using: true }
+                                            ]
+                                        });
+                                    }}>刷新</Button>
+                                </div>
+                            </Form>
+                            <Divider orientation="left">数据列</Divider>
+                        </div>
+                    ):null
+                }
+                <Table
+                    className='table-columnconfig'
+                    dataSource={dataSource.newOne.columns}
+                    columns={columns}
+                    locale={{
+                        emptyText: '未连接到数据对象'
+                    }}
+                />
+            </div>
+        );
+    }
 }
 
-function mapStateToProps({ present: {dataSource} }) {
+function mapStateToProps({ present: { dataSource } }) {
     return { dataSource }
 }
 
-export default connect(mapStateToProps)(DataSourceColumnConfig);
+export default connect(mapStateToProps)(DataSourceColumnConfig);
+
+class EditableCell extends React.Component {
+    state = {
+        dispatch: this.props.dispatch,
+        dataSource: this.props.dataSource,
+        fieldName: this.props.fieldName,
+        dataKey: this.props.dataKey,
+        type: this.props.type,
+        value: this.props.value,
+        editable: false,
+    }
+
+    getEditor = (e) => {
+        const { type, value } = this.state;
+        if(type == 'input') {
+            return (
+                <Input
+                    value={value}
+                    onChange={this.handleChange}
+                    onPressEnter={this.check}
+                    suffix={(
+                        <Icon
+                            type="check"
+                            className="editable-cell-icon-check"
+                            onClick={this.check}
+                        />
+                    )}
+                />
+            )
+        }else if(type == 'select') {
+            return (
+                <Select
+                    onChange={this.check}
+                >
+                    <SelectOption value='index'>索引</SelectOption>
+                    <SelectOption value='time'>时间</SelectOption>
+                    <SelectOption value='categorical'>类别</SelectOption>
+                    <SelectOption value='scale'>标量</SelectOption>
+                    <SelectOption value='ordinal'>序值</SelectOption>
+                    <SelectOption value='string'>字符串</SelectOption>
+                </Select>
+            )
+        }
+    }
+
+    handleChange = (e) => {
+        const value = e.target.value;
+        this.setState({ value });
+    }
+
+    check = () => {
+        const { dispatch, dataSource, fieldName, dataKey, value } = this.state;
+        this.setState({ editable: false });
+
+        let columns = dataSource.newOne.columns.map(c => {
+            if(c.key == dataKey) {
+                c[fieldName] = value;
+            }
+            return c;
+        });
+
+        dispatch({ type: 'dataSource/setNewModelField', name: 'columns', value: columns });
+    }
+
+    edit = () => {
+        this.setState({ editable: true });
+    }
+
+    render() {
+        const { value, editable } = this.state;
+        return (
+            <div className="editable-cell">
+                {
+                    editable ? this.getEditor() : (
+                        <div style={{ paddingRight: 24 }}>
+                            {value || ' '}
+                            <Icon
+                                type="edit"
+                                className="editable-cell-icon"
+                                onClick={this.edit}
+                            />
+                        </div>
+                    )
+                }
+            </div>
+        );
+    }
+}

+ 172 - 0
app/components/datasource/dataConnectBox.jsx

@@ -0,0 +1,172 @@
+import React from 'react'
+import { Modal, Form, Row, Col, Input, InputNumber, Select, Icon, Menu, Dropdown } from 'antd'
+const FormItem = Form.Item
+const SelectOption = Select.Option
+const OptionGroup = Select.OptGroup
+const InputGroup = Input.Group
+const SubMenu = Menu.SubMenu
+const MenuItem = Menu.Item
+const MenuItemGroup = Menu.ItemGroup;
+import { connect } from 'dva'
+import dataConnect from '../../models/dataConnect'
+
+const DataConnectBox = ({operation, dispatch, dataConnect, visibleBox, hideBox, form}) => {
+    const formItemLayout = {
+        labelCol: { span: 4 },
+        wrapperCol: { span: 20 },
+    };
+
+    const okHandler = () => {
+        if(operation == 'create') {
+            dispatch({ type: 'dataConnect/add' });
+        }else if(operation == 'edit') {
+            dispatch({ type: 'dataConnect/modify' });
+        }
+        hideBox();
+    }
+
+    const dataConnectLinkMenu = (
+        <Menu
+            className='menu-dataconnect-link'
+            onClick={() => {
+                dispatch({ type: 'dataConnect/setNewModelField', name: 'address', value: '1111adddd' });
+                dispatch({ type: 'dataConnect/setNewModelField', name: 'port', value: '1234' });
+                dispatch({ type: 'dataConnect/setNewModelField', name: 'type', value: {
+                    key: 'oracle',
+                    label: 'ORACLE'
+                } });
+                dispatch({ type: 'dataConnect/setNewModelField', name: 'dbName', value: 'orcl' });
+                dispatch({ type: 'dataConnect/setNewModelField', name: 'userName', value: 'UAS' });
+                dispatch({ type: 'dataConnect/setNewModelField', name: 'password', value: 'select!#%*(' });
+            }}
+        >
+            <MenuItem>1111</MenuItem>
+            <MenuItem>2222</MenuItem>
+            <MenuItem>33333</MenuItem>
+            <MenuItem>44</MenuItem>
+        </Menu>
+    );
+
+    return (
+        <Modal
+            className='newdataconnect-box'
+            title={`${operation=='create'?'新增':'修改'}数据库连接配置`}
+            visible={visibleBox}
+            onOk={() => {okHandler()}}
+            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.type}
+                        labelInValue={true}
+                        onChange={(value) => {
+                            dispatch({ type: 'dataConnect/setNewModelField', name: 'type', 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
+                                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>
+    )
+}
+
+function mapStateToProps({ present: { dataConnect } }) {
+    return { dataConnect: dataConnect };
+}
+
+export default connect(mapStateToProps)(DataConnectBox)

+ 11 - 2
app/components/datasource/dataSource.less

@@ -11,8 +11,8 @@
             }
         }
     }
-    .datasource-tab {
-        .datasource-table {
+    .datasource-tab, .dataconnect-tab {
+        .datasource-table, .dataconnect-table {
             .ant-table {
                 margin-top: 5px;
                 .ant-table-scroll {
@@ -69,6 +69,15 @@
                                         cursor: pointer;
                                     }
                                 }
+                                .action-col {
+                                    display: flex;
+                                    .operation {
+                                        cursor: pointer;
+                                    }
+                                    .operation:hover {
+                                        color: #40a9ff;
+                                    }
+                                }
                             }
                         }
                     }

+ 0 - 205
app/components/datasource/dataSourceBox.jsx

@@ -1,205 +0,0 @@
-import React from 'react'
-import { Modal, Form, Row, Col, Input, InputNumber, Select, Icon, Menu, Dropdown } from 'antd'
-const FormItem = Form.Item
-const SelectOption = Select.Option
-const OptionGroup = Select.OptGroup
-const InputGroup = Input.Group
-const SubMenu = Menu.SubMenu
-const MenuItem = Menu.Item
-const MenuItemGroup = Menu.ItemGroup;
-import { connect } from 'dva'
-import dataSource from '../../models/dataSource'
-
-const DataSourceBox = ({operation, dispatch, dataSource, visibleBox, hideBox, form}) => {
-    const formItemLayout = {
-        labelCol: { span: 4 },
-        wrapperCol: { span: 20 },
-    };
-
-    const okHandler = () => {
-        if(operation == 'create') {
-            dispatch({ type: 'dataSource/add' });
-        }else if(operation == 'edit') {
-            dispatch({ type: 'dataSource/modify' });
-        }
-        hideBox();
-    }
-
-    const dataSourceLinkMenu = (
-        <Menu
-            className='menu-datasource-link'
-            onClick={() => {
-                dispatch({ type: 'dataSource/setNewModelField', name: 'address', value: '1111adddd' });
-                dispatch({ type: 'dataSource/setNewModelField', name: 'port', value: '1234' });
-                dispatch({ type: 'dataSource/setNewModelField', name: 'type', value: {
-                    key: 'oracle',
-                    label: 'ORACLE'
-                } });
-                dispatch({ type: 'dataSource/setNewModelField', name: 'dbName', value: 'orcl' });
-                dispatch({ type: 'dataSource/setNewModelField', name: 'userName', value: 'UAS' });
-                dispatch({ type: 'dataSource/setNewModelField', name: 'password', value: 'select!#%*(' });
-            }}
-        >
-            <MenuItem>1111</MenuItem>
-            <MenuItem>2222</MenuItem>
-            <MenuItem>33333</MenuItem>
-            <MenuItem>44</MenuItem>
-        </Menu>
-    );
-
-    return (
-        <Modal
-            className='newdatasource-box'
-            title={`${operation=='create'?'新增':'修改'}数据源配置`}
-            visible={visibleBox}
-            onOk={() => {okHandler()}}
-            onCancel={hideBox}
-            maskClosable={false}
-            destroyOnClose={true}
-        >
-            <Row>
-                <Col span={12}>
-                    <Form size='small'>
-                        <FormItem label='数据源名称' {...formItemLayout}>
-                            <Input
-                                value={dataSource.newOne.name}
-                                onChange={(e) => { dispatch({ type: 'dataSource/setNewModelField', name: 'name', value: e.target.value }) }}>
-                            </Input>
-                        </FormItem>
-                        <Row>
-                            <Col span={19}>
-                                <FormItem label='数据库地址' {...{
-                                    labelCol: { span: 5 },
-                                    wrapperCol: { span: 19 }
-                                }}>
-                                    <Input
-                                        value={dataSource.newOne.address}
-                                        onChange={(e) => {
-                                            dispatch({ type: 'dataSource/setNewModelField', name: 'address', value: e.target.value });
-                                        }}
-                                        prefix={
-                                            <Dropdown
-                                                trigger={['click']}
-                                                overlay={dataSourceLinkMenu}
-                                            >
-                                                <div style={{cursor: 'pointer'}}><Icon type='down' /></div>
-                                            </Dropdown>
-                                        }
-                                    />
-                                </FormItem>
-                            </Col>
-                            <Col span={5}>
-                                <FormItem className='input-port' label='端口' {...{
-                                    labelCol: { span: 12 },
-                                    wrapperCol: { span: 12 }
-                                }}>
-                                    <InputNumber
-                                        value={dataSource.newOne.port}
-                                        onChange={(value) => {
-                                            dispatch({ type: 'dataSource/setNewModelField', name: 'port', value: value });
-                                        }}
-                                    />
-                                </FormItem>
-                            </Col>
-                        </Row>
-                        <FormItem label='数据库类型' {...formItemLayout}>
-                            <Select
-                                value={dataSource.newOne.type}
-                                labelInValue={true}
-                                onChange={(value) => {
-                                    dispatch({ type: 'dataSource/setNewModelField', name: 'type', value: value} );
-                                }}
-                            >
-                                <SelectOption value='oracle'>
-                                    ORACLE
-                                </SelectOption>
-                                <SelectOption value='mysql'>
-                                    MYSQL
-                                </SelectOption>
-                                <SelectOption value='sqlserver'>
-                                    SQLSERVER
-                                </SelectOption>
-                                <SelectOption value='sqlite'>
-                                    SQLITE
-                                </SelectOption>
-                            </Select>
-                        </FormItem>
-                        <FormItem label='数据库名' {...formItemLayout}>
-                            <Input
-                                value={dataSource.newOne.dbName}
-                                onChange={(e) => {
-                                    dispatch({ type: 'dataSource/setNewModelField', name: 'dbName', value: e.target.value });
-                                }}
-                            />
-                        </FormItem>
-                        <Row>
-                            <Col span={12}>
-                                <FormItem label='用户名' {...{
-                                    labelCol: { span: 8 },
-                                    wrapperCol: { span: 16 }
-                                }}>
-                                    <Input
-                                        value={dataSource.newOne.userName}
-                                        onChange={(e) => {
-                                            dispatch({ type: 'dataSource/setNewModelField', name: 'userName', value: e.target.value });
-                                        }}
-                                    />
-                                </FormItem>
-                            </Col>
-                            <Col span={12}>
-                                <FormItem label='密码' {...{
-                                    labelCol: { span: 8 },
-                                    wrapperCol: { span: 16 }
-                                }}>
-                                    <Input
-                                        type='password'
-                                        value={dataSource.newOne.password}
-                                        onChange={(e) => {
-                                            dispatch({ type: 'dataSource/setNewModelField', name: 'password', value: e.target.value });
-                                        }}
-                                    />
-                                </FormItem>
-                            </Col>
-                        </Row>
-                        <FormItem className='textarea-target' label='加载对象' {...formItemLayout}>
-                            <Input.TextArea
-                                placeholder='输入表名或查询SQL'
-                                autosize={{ minRows: 3 }}
-                            />
-                        </FormItem>
-                        <FormItem label='自定义标签' {...formItemLayout}>
-                            <Select
-                                mode="tags"
-                                placeholder='多个标签使用逗号或空格分隔'
-                                tokenSeparators={[',', ' ']}
-                                value={dataSource.newOne.tags}
-                                dropdownStyle={{display: 'none'}}
-                                onChange={(value) => {
-                                    dispatch({ type: 'dataSource/setNewModelField', name: 'tags', value: value });
-                                }}
-                            >
-                            </Select>
-                        </FormItem>
-                        <FormItem className='textarea-desc' label='说明' {...formItemLayout}>
-                            <Input.TextArea
-                                autosize={{ minRows: 2, maxRows: 2 }}
-                                value={dataSource.newOne.description}
-                                onChange={(e) => {
-                                    dispatch({ type: 'dataSource/setNewModelField', name: 'description', value: e.target.value });
-                                }}
-                            />
-                        </FormItem>
-                    </Form>
-                </Col>
-                <Col span={12}>
-                </Col>
-            </Row>
-        </Modal>
-    )
-}
-
-function mapStateToProps({ present: { dataSource } }) {
-    return { dataSource: dataSource };
-}
-
-export default connect(mapStateToProps)(DataSourceBox)

+ 46 - 26
app/components/datasource/dataSourceDetail.jsx

@@ -1,7 +1,9 @@
 import React from 'react'
-import { Layout, Steps, Button } from 'antd'
+import { connect } from 'dva'
+import { Layout, Steps, Button, message, Tabs } from 'antd'
 const { Header, Content } = Layout
 const Step = Steps.Step
+const TabPane = Tabs.TabPane
 import { Link } from 'react-router-dom'
 import BaseConfig from './baseConfig'
 import ConnectConfig from './connectConfig'
@@ -14,10 +16,18 @@ class DataSourceDetail extends React.Component {
     constructor(props) {
         super(props);
         this.state = {
+            mode: props.match.params.code&&props.match.params.code=='create'?'create':'modify',
+            type: props.match.params.type,
+            code: props.match.params.code,
             current: 0
         }
     }
 
+    componentDidMount() {
+        this.props.dispatch({ type: 'dataSource/setNewModelField', name: 'type', value: this.props.match.params.type });
+        this.props.dispatch({ type: 'dataSource/setNewModelField', name: 'code', value: this.props.match.params.code });
+    }
+
     next() {
         const current = this.state.current + 1;
         this.setState({ current });
@@ -29,10 +39,10 @@ class DataSourceDetail extends React.Component {
     }
 
     render() {
-        const { current } = this.state;
+        const { mode, current } = this.state;
         const steps = [{
             title: '属性配置',
-            content: <BaseConfig />
+            content: <BaseConfig/>
         }, {
             title: '数据列配置',
             content: <ColumnConfig />
@@ -43,32 +53,42 @@ class DataSourceDetail extends React.Component {
         return (
             <Layout className='layout-datasource-detail'>
                 <Content className='content'>
-                    <Steps className='steps-body' current={current}>
-                        {steps.map(item => <Step key={item.title} title={item.title} />)}
-                    </Steps>
-                    <div className="steps-content">
-                        {steps[current].content}
-                    </div>
-                    <div className="steps-action">
-                        {
-                            current > 0 && (
-                            <Button onClick={() => this.prev()}>
-                                上一步
-                            </Button>)
-                        }
-                        {
-                            current < steps.length - 1
-                            && <Button type="primary" onClick={() => this.next()}>下一步</Button>
-                        }
-                        {
-                            current === steps.length - 1
-                            && <Button type="primary" onClick={() => message.success('Processing complete!')}>完成</Button>
-                        }
-                    </div>
+                    {mode == 'create' ? (<div>
+                        <Steps className='steps-body' current={current}>
+                            {steps.map((item,index) => <Step key={'step-' + index} title={item.title} />)}
+                        </Steps>
+                        <div className="steps-content">
+                            {steps[current].content}
+                        </div>
+                        <div className="steps-action">
+                            {
+                                current > 0 && (
+                                <Button onClick={() => this.prev()}>
+                                    上一步
+                                </Button>)
+                            }
+                            {
+                                current < steps.length - 1
+                                && <Button type="primary" onClick={() => this.next()}>下一步</Button>
+                            }
+                            {
+                                current === steps.length - 1
+                                && <Button type="primary" onClick={() => message.success('Processing complete!')}>完成</Button>
+                            }
+                        </div>
+                    </div>) : (
+                        <Tabs defaultActiveKey="tab-0" type="card">
+                            {steps.map((item, index) => {
+                                return <TabPane className='tab-datasource' key={'tab-' + index} tab={item.title}>
+                                    {item.content}
+                                </TabPane>
+                            })}
+                        </Tabs>
+                    )}
                 </Content>
             </Layout>
         )
     }
 }
 
-export default DataSourceDetail;
+export default connect()(DataSourceDetail);

+ 46 - 2
app/components/datasource/dataSourceDetail.less

@@ -4,8 +4,8 @@
         .steps-body {
             padding: 0 20%;
         }
-        .steps-content {
-            padding: 10px 0;
+        .steps-content,.tab-datasource {
+            padding: 10px 30px;
             .ant-divider {
                 margin: 10px 0;
                 .ant-divider-inner-text {
@@ -26,6 +26,7 @@
                     display: flex;
                     justify-content: flex-end;
                     .ant-dropdown-trigger {
+                        cursor: pointer;
                         margin-right: 4px;
                         color: rgb(24, 144, 255);
                         height: 32px;
@@ -38,11 +39,54 @@
                 textarea {
                     margin-top: 4px;
                 }
+                .upload {
+                    .ant-upload-drag {
+                        margin: 0 auto;
+                        width: 80%;
+                        margin: 0 0 0 16.666666%;
+                        width: 83%;
+                    }
+                    .ant-upload-list {
+                        margin: 0 auto;
+                        width: 80%;
+                        margin: 0 0 0 16.666666%;
+                        width: 83%;
+                    }
+                }
             }
             .table-columnconfig {
                 .ant-table-thead > tr > th, .ant-table-tbody > tr > td {
                     padding: 10px 16px;
                 }
+                .editable-cell {
+                    position: relative;
+                    input {
+                        padding-top: 0;
+                        padding-bottom: 0;
+                    }
+                }
+                  
+                .editable-cell-icon,
+                .editable-cell-icon-check {
+                    cursor: pointer;
+                }
+                  
+                .editable-cell-icon {
+                    line-height: 14px;
+                    position: absolute;
+                    right: 0;
+                    top: 50%;
+                    margin-top: -7px;
+                    display: inline-block;
+                }
+                  
+                td:hover .editable-cell-icon {
+                }
+                  
+                .editable-cell-icon:hover,
+                .editable-cell-icon-check:hover {
+                    color: #108ee9;
+                }
             }
         }
         .steps-action {

+ 100 - 55
app/components/datasource/datasource.jsx

@@ -1,41 +1,55 @@
 import React from 'react'
-import { Tabs, Input, Button, Table, Icon, Tag, Menu, Dropdown, Modal } from 'antd'
+import { routerRedux } from 'dva/router'
+import { Tabs, Input, Button, Table, Icon, Tag, Menu, Dropdown, Divider } from 'antd'
 const { TabPane } = Tabs
 const { Search } = Input
 import { connect } from 'dva'
 import { Link } from 'react-router-dom'
-import DataSourceBox from './dataSourceBox'
+import DataConnectBox from './dataConnectBox'
 import CreateBox from './createBox'
 import dataSource from '../../models/dataSource'
+import dataConnect from '../../models/dataConnect'
 import './dataSource.less'
 
 class DataSource extends React.Component {
     constructor(props) {
         super(props);
         this.state = {
+            activeTab: 'dataSource',
             operation: 'create', // 打开数据编辑界面的类型
             visibleCreateBox: false,
             visibleBox: false, // 数据编辑界面显示标识
-            selectedCode: -1, // 当前选中的table行code
+            selectedDataSourceCode: -1, // 当前选中的dataSource的code
+            selectedDataConnectCode: -1, // 当前选中的dataConnect的code
             filterDropdownVisible: false,
             search: {} // 搜索条件
         }
     };
 
+    componentDidMount() {
+        const mainContent = document.getElementsByClassName('main-content')[0];
+        const tabBar = mainContent.getElementsByClassName('ant-tabs-bar')[0];
+        const tableHeader = mainContent.getElementsByClassName('ant-table-header')[0];
+        const tableBody = mainContent.getElementsByClassName('ant-table-body')[0];
+
+        tableBody.style.maxHeight=`${mainContent.offsetHeight - tabBar.offsetHeight - tableHeader.offsetHeight - 38}px`;
+        console.log(tableBody.style.maxHeight);
+    }
+
     showCreateBox = () => {
         this.setState({
             visibleCreateBox: true
         });
     }
 
-    showDataSourceBox = (o) => {
+    showDataConnectBox = (o) => {
         this.setState({
             operation: o,
             visibleBox: true
         });
     }
 
-    hideBox = () => {
+    hideDataConnectBox = () => {
         this.setState({
             visibleBox: false
         });
@@ -58,22 +72,22 @@ class DataSource extends React.Component {
 
     render() {
         
-        const { dataSource, dispatch } = this.props;
-        const { search, visibleBox, operation, selectedCode } = this.state;
+        const { dataSource, dataConnect, dispatch } = this.props;
+        const { activeTab, search, visibleBox, operation, selectedDataSourceCode, selectedDataConnectCode } = this.state;
         
         const moreOperatingMenu = (
             <Menu className='operationmenu'>
                 <Menu.Item
                     onClick={(e) => {
-                        let selectedModel = dataSource.list.find((i) => { return i.code == selectedCode })
-                        dispatch({ type: 'dataSource/setNewModel', model: selectedModel });
-                        this.showDataSourceBox('edit');
+
+                        let selectedModel = dataSource.list.find((i) => { return i.code == selectedDataSourceCode })
+                        dispatch({type: 'main/redirect', path: {pathname: '/datasource/'+ selectedModel.type +'/' + selectedModel.code}});
                     }}>
                     <Icon type="info-circle-o" />属性设置
                 </Menu.Item>
                 <Menu.Item>
                     {/* <Link to='/dataSource/1111'> */}
-                        <Icon type="home" /><Icon type="table" />数据列设置
+                        <Icon type="table" />数据列设置
                     {/* </Link> */}
                 </Menu.Item>
                 <Menu.Item><Icon type="search" />预览数据</Menu.Item>
@@ -81,13 +95,13 @@ class DataSource extends React.Component {
                 <Menu.Divider />
                 <Menu.Item
                     onClick={(e) => {
-                        let selectedModel = dataSource.list.find((i) => { return i.code == selectedCode })
+                        let selectedModel = dataSource.list.find((i) => { return i.code == selectedDataSourceCode })
                         dispatch({ type: 'dataSource/delete', model: selectedModel });
                     }}
                 ><Icon type="delete" />删除</Menu.Item>
             </Menu>
         );
-        const columns = [{
+        const dataSourceColumns = [{
             title: '名称',
             dataIndex: 'name',
             key: 'name',
@@ -164,24 +178,37 @@ class DataSource extends React.Component {
             ),
             width: 80
         }];
-        const data = [{
-            key: '1',
-            name: '销售数据',
-            type: 'oracle',
-            tag: '销售,常常常常文本',
-            description: '这是一个测试数据',
-            creator: 'zhuth',
-            createTime: new Date('2018-07-01 08:33:18').format('yyyy-MM-dd hh:mm:ss'),
-            chartSum: 9
+
+        const dataConnectColumns = [{
+            title: '名称',
+            dataIndex: 'name',
+            key: 'name',
+            width: 100,
+            render: (text, record) => {
+                return <div className='datasource-name'>
+                    <div className={`datasource-type type-${record.type.key}`}></div>
+                    <div>{text}</div>
+                </div>
+            }
         }, {
-            key: '2',
-            name: '采购数据',
-            type: 'sqlserver',
-            tag: '采购',
-            description: '这是一个测试数据',
-            creator: 'zhuth',
-            createTime: new Date('2018-07-04 10:38:29').format('yyyy-MM-dd hh:mm:ss'),
-            chartSum: 7
+            title: '说明',
+            dataIndex: 'description',
+            key: 'description',
+            width: 100
+        }, {
+            title: '操作',
+            key: 'action',
+            width: 300,
+            render: () => (
+                <div className='action-col'>
+                    <div className='operation'><Icon type="info-circle-o"/>属性</div>
+                    <div className='operation'><Divider type="vertical" /></div>
+                    <div className='operation'><Icon type="plus"/>创建数据源</div>
+                    <div className='operation'><Divider type="vertical" /></div>
+                    <div className='operation'><Icon type="delete"/>删除</div>
+                </div>
+            ),
+            width: 80
         }];
 
         return (
@@ -192,52 +219,70 @@ class DataSource extends React.Component {
                 tabBarExtraContent={
                     <div className='datasource-tabs-tools'>
                         <Button onClick={()=>{
-                            dispatch({type: 'dataSource/testData'});
+                            dispatch({type: activeTab=='dataConnect'?'dataConnect/testData':'dataSource/testData'});
                         }}>测试数据</Button>
                         <Search
                             placeholder="请输入关键字"
                             onSearch={value => console.log(value)}
                         />
-                        <Button>
-                            <Link to='/dataSource/111'><Icon type="plus" />添加数据源</Link>
+                        <Dropdown overlay={(
+                            <Menu onClick={(item, key, keyPath) => {
+                                const type = item.key;
+                                dispatch({ type: 'dataSource/setNewModelField', name: 'type', value: type });
+                                dispatch({type: 'main/redirect', path: {pathname: '/datasource/'+ type +'/create'}});
+                            }}>
+                                <Menu.Item key='database'>数据库</Menu.Item>
+                                <Menu.Item key='file'>文件</Menu.Item>
+                            </Menu>
+                        )} trigger={['click']}>
+                            <Button style={{ display: activeTab=='dataConnect'?'none':'inline-block' }}>
+                                <Icon type="plus" />添加数据源
+                            </Button>
+                        </Dropdown>
+                        <Button style={{ display: activeTab=='dataConnect'?'inline-block':'none' }} onClick={(e) => {
+                            this.showDataConnectBox()
+                        }}>
+                            <Icon type="plus" />添加数据连接
                         </Button>
-                        <DataSourceBox operation={operation} visibleBox={visibleBox} hideBox={this.hideBox} />
-                        <Modal
-                            title='添加数据源'
-                            maskClosable={false}
-                            destroyOnClose={true}
-                            visible={this.state.visibleCreateBox}
-                            onOk={() => { this.setState({ visibleCreateBox: false }) }}
-                            onCancel={() => { this.setState({ visibleCreateBox: false }) }}
-                        >
-                            <CreateBox />
-                        </Modal>
+                        <DataConnectBox operation={operation} visibleBox={visibleBox} hideBox={this.hideDataConnectBox} />
                     </div>
                 }
+                onChange={(key) => {
+                    this.setState({
+                        activeTab: key == '1' ? 'dataSource' : 'dataConnect'
+                    });
+                }}
             >
-                <TabPane className='datasource-tab public-datasource' tab="数据源" key="1" >
+                <TabPane className='datasource-tab' tab="数据源" key="1" >
                     <Table
-                        className='datasource-table public-datasource-table'
-                        columns={columns}
+                        className='datasource-table datasource-table'
+                        columns={dataSourceColumns}
                         dataSource={dataSource.list}
                         loading={false}
                         size='small'
-                        scroll={{x: false, y: 471}} // TODO 需要计算
+                        scroll={{x: false, y: 471}}
                         pagination={false}
                         onRow={(record) => {
                             return {
-                                onClick: () => {this.setState({ selectedCode:  record.code})}
+                                onClick: () => {this.setState({ selectedDataSourceCode:  record.code})}
                             }
                         }}
                     />
                 </TabPane>
-                <TabPane className='datasource-tab my-datasource' tab="数据库连接" key="2" >
+                <TabPane className='dataconnect-tab dataconnect' tab="数据库连接" key="2" >
                     <Table
-                        className='datasource-table my-datasource-table'
-                        columns={columns}
-                        dataSource={dataSource.my}
+                        className='dataconnect-table dataconnect-table'
+                        columns={dataConnectColumns}
+                        dataSource={dataConnect.list}
                         loading={false}
                         size='small'
+                        scroll={{x: false, y: 471}}
+                        pagination={false}
+                        onRow={(record) => {
+                            return {
+                                onClick: () => {this.setState({ selectedDataConnectCode:  record.code})}
+                            }
+                        }}
                     />
                 </TabPane>
             </Tabs>
@@ -247,8 +292,8 @@ class DataSource extends React.Component {
     }
 }
 
-function mapStateToProps({present: {dataSource}}) {
-    return { dataSource }
+function mapStateToProps({present: {dataSource, dataConnect}}) {
+    return { dataSource, dataConnect }
 }
 
 export default connect(mapStateToProps)(DataSource)

+ 4 - 0
app/index.js

@@ -1,7 +1,9 @@
 import dva from 'dva'
 import undoable from 'redux-undo'
+import main from './models/main'
 import chartDesigner from './models/chartDesigner'
 import dataSource from './models/dataSource'
+import dataConnect from './models/dataConnect'
 import './utils/baseUtils'
 import './index.less'
 
@@ -17,8 +19,10 @@ const app = dva({
 // app.use({});
 
 // 3. Model
+app.model(main); // 通用action
 app.model(chartDesigner); // 图表
 app.model(dataSource); // 数据源
+app.model(dataConnect); // 数据连接
 
 // 4. Router
 app.router(require('./routes/router'));

+ 2 - 2
app/models/chartDesigner.js

@@ -160,8 +160,8 @@ export default {
                 },
             });
         },
-        * redirect ({ payload }, { put }) {
-            yield put(routerRedux.push('/'));
+        * redirect (action, { put }) {
+            yield put(routerRedux.push(action.path || '/'));
         },
     },
     subscriptions: {

+ 73 - 0
app/models/dataConnect.js

@@ -0,0 +1,73 @@
+export default {
+    namespace: 'dataConnect',
+    state: {
+        list: [],
+        newOne: {}
+    },
+    reducers: {
+        testData(state, action) {
+            let list = state.list;
+            for(let i = 0; i < 4; i++) {
+                let newOne = {
+                    name: 'tttttt',
+                    type: {key: 'oracle', label: 'ORACLE'},
+                    address: '2',
+                    port: '3',
+                    userName: '2222',
+                    password: 'aaaww',
+                    desc: ' dddddddddddddddddddddd'
+                };
+                newOne.key = new Date().getMilliseconds()+(Math.random()*100).toFixed(0)+i;
+                newOne.code = new Date().getMilliseconds()+(Math.random()*100).toFixed(0)+i;
+                list.push(newOne);
+            }
+            return Object.assign({}, state, {list});
+        },
+        add(state, action) {
+            let newOne = Object.assign({}, state.newOne);
+            let list = state.list;
+            newOne.key = new Date().getMilliseconds()+(Math.random()*100).toFixed(0);
+            newOne.code = new Date().getMilliseconds()+(Math.random()*100).toFixed(0);
+            newOne.creator = 'zhuth';
+            newOne.createTime = new Date();
+            list.push(newOne);
+            return Object.assign({}, state, {list});
+        },
+        modify(state, action) {
+            const { newOne } = state;
+            let list = state.list;
+            for(let i = 0; i < list.length; i++) {
+                if(list[i].code == newOne.code) {
+                    list[i] = Object.assign({}, newOne);
+                    break;
+                }
+            }
+            return Object.assign({}, state, {list});
+        },
+        delete(state, action) {
+            const { model } = action;
+            let { list } = state;
+            for(let i = 0; i < list.length; i++) {
+                if(list[i].code == model.code) {
+                    list.splice(i, 1);
+                }
+            }
+            return Object.assign({}, state, {list});
+
+        },
+        setNewModelField(state, action) {
+            const { name, value } = action;
+            let newOne = state.newOne;
+            newOne[name] = value;
+            return Object.assign({}, state, {newOne});
+        },
+        setNewModel(state, action) {
+            const { model } = action;
+            let newOne = Object.assign({}, model);
+            return Object.assign({}, state, {newOne});
+        },
+        resetNewModel(state, action) {
+            return Object.assign({}, state, {newOne: {}});
+        }
+    }
+};

+ 7 - 7
app/models/dataSource.js

@@ -1,11 +1,8 @@
 export default {
     namespace: 'dataSource',
     state: {
-        origin: { type:{key:'',label:''}, address:'', port:'', dbName:'', table:[], userName:'', password:'', desc:'' },
         newOne: {},
         list: [],
-        public: [],
-        my: []
     },
     reducers: {
         testData(state, action) {
@@ -13,7 +10,8 @@ export default {
             for(let i = 0; i < 4; i++) {
                 let newOne = {
                     name: 'aldjalsdal',
-                    type: {key: 'oracle', label: 'ORACLE'},
+                    type: i%2?'file':'database',
+                    dbType: {key: 'oracle', label: 'ORACLE'},
                     address: '2',
                     port: '3',
                     target: 'select * from employee',
@@ -22,10 +20,10 @@ export default {
                     userName: '2222',
                     password: 'aaaww',
                     tags: ['tttt', 'accc'],
-                    desc: ' dddddddddddddddddddddd'
+                    description: ' dddddddddddddddddddddd'
                 };
                 newOne.key = new Date().getMilliseconds()+(Math.random()*100).toFixed(0)+i;
-                newOne.code = new Date().getMilliseconds()+(Math.random()*100).toFixed(0);
+                newOne.code = new Date().getMilliseconds()+(Math.random()*100).toFixed(0)+i;
                 list.push(newOne);
             }
             return Object.assign({}, state, {list});
@@ -66,7 +64,9 @@ export default {
             const { name, value } = action;
             let newOne = state.newOne;
             newOne[name] = value;
-            return Object.assign({}, state, {newOne});
+            let obj = Object.assign({}, state, {newOne});
+            console.log(obj);
+            return obj;
         },
         setNewModel(state, action) {
             const { model } = action;

+ 14 - 0
app/models/main.js

@@ -0,0 +1,14 @@
+import { routerRedux } from 'dva/router'
+
+export default {
+    namespace: 'main',
+    state: {
+    },
+    reducers: {
+    },
+    effects: {
+        * redirect (action, { put }) {
+            yield put(routerRedux.push(action.path || '/'));
+        },
+    }
+};

+ 2 - 2
app/routes/mainLayout.js

@@ -19,8 +19,8 @@ const MainLayout = (history) => {
             <Content className='main-content'>
                 <Switch>
                     <Route exact path='/' component={Welcome}/>
-                    <Route exact path='/dataSource' component={DataSource}/>
-                    <Route path='/dataSource/:code' component={DataSourceDetail}/>
+                    <Route exact path='/datasource' component={DataSource}/>
+                    <Route path='/datasource/:type/:code' component={DataSourceDetail}/>
                 </Switch>
             </Content>
         </Layout>

+ 1 - 1
app/routes/router.js

@@ -11,7 +11,7 @@ function RouterConfig({ history }) {
     <LocaleProvider locale={zhCN}>
       <Router history={history}>
         <Switch>
-          <Route path='/chartDesigner' component={ChartDesigner}/>
+          <Route path='/chartdesigner' component={ChartDesigner}/>
           <Route path='/' component={MainLayout}/>
         </Switch>
       </Router>