Переглянути джерело

数据连接界面卡片布局/数据连接校验逻辑

zhuth 6 роки тому
батько
коміт
ad86f66ccb

+ 107 - 142
src/components/dataConnect/list.jsx

@@ -1,9 +1,10 @@
 import React from 'react'
-import { Layout, Row, Col, Input, Button, Table, Icon, Menu, Dropdown, Card } from 'antd'
+import { Layout, Row, Col, Input, Button, Icon, Menu, Dropdown, Card } from 'antd'
 import { connect } from 'dva'
 import DeleteBox from '../common/deleteBox/deleteBox'
 import DataConnectBox from '../dataSourceDetail/dataConnectBox'
 import './list.less'
+const CardGrid = Card.Grid
 const { Content } = Layout
 const { Search } = Input
 
@@ -11,26 +12,25 @@ class DataConnect extends React.Component {
     constructor(props) {
         super(props);
         this.state = {
-            selectedRecord: null, // 当前选中的dataSource
             visibleDeleteBox: false,
         }
     };
     componentDidMount() {
         const { dispatch } = this.props;
-        this.setScrollTableHeight();
+        // this.setScrollTableHeight();
         dispatch({ type: 'dataConnect/fetchList' });
     }
 
     /**
      * 根据视图设置表格高度以呈现滚动条
      */
-    setScrollTableHeight() {
-        const mainContent = document.getElementsByClassName('main-content')[0];
-        const toolbar = mainContent.getElementsByClassName('dataconnect-tools')[0];
-        const tableHeader = mainContent.getElementsByClassName('ant-table-header')[0];
-        const tableBody = mainContent.getElementsByClassName('ant-table-body')[0];
-        tableBody.style.maxHeight=`${mainContent.offsetHeight - toolbar.offsetHeight - tableHeader.offsetHeight - 58}px`;
-    }
+    // setScrollTableHeight() {
+    //     const mainContent = document.getElementsByClassName('main-content')[0];
+    //     const toolbar = mainContent.getElementsByClassName('dataconnect-tools')[0];
+    //     const tableHeader = mainContent.getElementsByClassName('ant-table-header')[0];
+    //     const tableBody = mainContent.getElementsByClassName('ant-table-body')[0];
+    //     tableBody.style.maxHeight=`${mainContent.offsetHeight - toolbar.offsetHeight - tableHeader.offsetHeight - 58}px`;
+    // }
 
     onSearch(list, text) {
         const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
@@ -58,29 +58,27 @@ class DataConnect extends React.Component {
         this.setState({ visibleGroupMenu: flag });
     }
 
-    render() {
-        
+    generateCard() {
         const { dataConnect, dispatch } = this.props;
-        const { selectedRecord, visibleDeleteBox } = this.state;
-
+        const { selected } = dataConnect;
         const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
         let filterLabel = dataConnect.filterLabel.replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
 
-        const moreOperatingMenu = (
-            <Menu className='operationmenu' visible={true}>
-                {selectedRecord && <Menu.Item onClick={(e) => {
-                    dispatch({ type: 'dataConnect/setNewModel', model: selectedRecord });
+        const operationMenu = (
+            <Menu className='menu-operation'>
+                <Menu.Item onClick={(e) => {
+                    dispatch({ type: 'dataConnect/setNewModel', model: selected });
                     dispatch({ type: 'dataConnect/setNewModelFields', fields: [
                         { name: 'visibleBox', value: true },
                         { name: 'boxOperation', value: 'modify' }
                     ] });
                     }}>
                     <Icon type="info-circle-o" />属性设置
-                </Menu.Item>}
+                </Menu.Item>
                 <Menu.Divider />
-                { selectedRecord && <Menu.Item
+                <Menu.Item
                     onClick={(e) => {
-                        dispatch({ type: 'dataConnect/setNewModel', model: { ...selectedRecord, code: null } });
+                        dispatch({ type: 'dataConnect/setNewModel', model: { ...selected, code: null } });
                         dispatch({ type: 'dataConnect/setNewModelFields', fields: [
                             { name: 'visibleBox', value: true },
                             { name: 'boxOperation', value: 'create' }
@@ -88,115 +86,98 @@ class DataConnect extends React.Component {
                     }}
                 >
                     <Icon type="copy" />复制新增
-                </Menu.Item>}
-                { selectedRecord && <Menu.Item
+                </Menu.Item>
+                <Menu.Item
                     onClick={(e) => {
                         this.setState({ visibleDeleteBox: true})
                     }}
                 >
                     <Icon type="delete" />删除
-                </Menu.Item>}
+                </Menu.Item>
             </Menu>
-        );
-        const dataConnectColumns = [{
-            title: '连接名',
-            dataIndex: 'name',
-            key: 'name',
-            width: 200,
-            render: (text, record) => {
-                return <div className='dataconnect-name'>
-                    <div>
-                        <span>
-                            { filterLabel ?
-                                ((text || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
-                                    return (
-                                        fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
-                                        <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
-                                        fragment
-                                    )
-                                }
-                                )) : text
-                            }
-                        </span>
-                    </div>
-                </div>
-            }
-        }, {
-            title: '数据库类型',
-            dataIndex: 'dbType',
-            key: 'dbType',
-            width: 120
-        }, {
-            title: '数据库地址',
-            dataIndex: 'address',
-            key: 'address',
-            width: 120
-        }, {
-            title: '端口',
-            dataIndex: 'port',
-            key: 'port',
-            width: 100
-        }, {
-            title: '数据库名',
-            dataIndex: 'dbName',
-            key: 'dbName',
-            width: 100
-        }, {
-            title: '用户名',
-            dataIndex: 'userName',
-            key: 'userName',
-            width: 150
-        }, {
-            title: '说明',
-            dataIndex: 'description',
-            key: 'description',
-            render: (text, record) => {
-                return <div className='dataconnect-name'>
-                    <div>
-                        <span>
-                            { filterLabel ?
-                                ((text || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
-                                    return (
-                                        fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
-                                        <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
-                                        fragment
-                                    )
+        )
+
+        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 className='label'>
+                                { filterLabel ?
+                                    ((l.name || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
+                                        return (
+                                            fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
+                                            <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
+                                            fragment
+                                        )
+                                    }
+                                    )) : l.name
                                 }
-                                )) : text
-                            }
-                        </span>
+                            </Col>
+                        </Row>
+                    }
+                    onClick={() => {
+                        // 选中项设置
+                        dispatch({ type: 'dataConnect/setSelected', selected: l });
+                        dispatch({ type: 'dataConnect/setNewModel', model: l });
+                        dispatch({ type: 'dataConnect/setNewModelFields', fields: [
+                            { name: 'visibleBox', value: true },
+                            { name: 'boxOperation', value: 'modify' }
+                        ] });
+                    }}
+                >
+                    <div className='content'>
+                        <Row className='address'>
+                            {l.address}
+                        </Row>
+                        <Row className='username'>
+                            {l.userName}
+                        </Row>
+                        <Row className='bottom' type='flex' justify='space-between'>
+                            <Col>{l.dbType}</Col>
+                            <Col>
+                                <Dropdown overlay={operationMenu} trigger={['click']}>
+                                    <Icon type="ellipsis" />
+                                </Dropdown>
+                            </Col>
+                        </Row>
                     </div>
-                </div>
-            }
-        }, {
-            title: '操作',
-            key: 'action',
-            render: (text, record, index) => (
-                <Dropdown code={record.code} overlay={moreOperatingMenu} trigger={['click']} >
-                    <Icon type="setting" />
-                </Dropdown>
-            ),
-            width: 50
-        }];
+                </Card>
+            </CardGrid>
+        ))
 
-        return ( 
-            <Layout className='dataconnect-view'>
+        if(cards.length === 0) {
+            return (<div className="ant-empty ant-empty-normal"><div className="ant-empty-image"><img alt="暂无数据" src="data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNjQiIGhlaWdodD0iNDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CiAgPGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMCAxKSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgIDxlbGxpcHNlIGZpbGw9IiNGNUY1RjUiIGN4PSIzMiIgY3k9IjMzIiByeD0iMzIiIHJ5PSI3Ii8+CiAgICA8ZyBmaWxsLXJ1bGU9Im5vbnplcm8iIHN0cm9rZT0iI0Q5RDlEOSI+CiAgICAgIDxwYXRoIGQ9Ik01NSAxMi43Nkw0NC44NTQgMS4yNThDNDQuMzY3LjQ3NCA0My42NTYgMCA0Mi45MDcgMEgyMS4wOTNjLS43NDkgMC0xLjQ2LjQ3NC0xLjk0NyAxLjI1N0w5IDEyLjc2MVYyMmg0NnYtOS4yNHoiLz4KICAgICAgPHBhdGggZD0iTTQxLjYxMyAxNS45MzFjMC0xLjYwNS45OTQtMi45MyAyLjIyNy0yLjkzMUg1NXYxOC4xMzdDNTUgMzMuMjYgNTMuNjggMzUgNTIuMDUgMzVoLTQwLjFDMTAuMzIgMzUgOSAzMy4yNTkgOSAzMS4xMzdWMTNoMTEuMTZjMS4yMzMgMCAyLjIyNyAxLjMyMyAyLjIyNyAyLjkyOHYuMDIyYzAgMS42MDUgMS4wMDUgMi45MDEgMi4yMzcgMi45MDFoMTQuNzUyYzEuMjMyIDAgMi4yMzctMS4zMDggMi4yMzctMi45MTN2LS4wMDd6IiBmaWxsPSIjRkFGQUZBIi8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K"/></div><p className="ant-empty-description">暂无数据</p></div>)
+        }
+        
+        return cards;
+    }
+
+    render() {
+        const { dataConnect, dispatch } = this.props;
+        const { selected } = dataConnect;
+        const { visibleDeleteBox } = this.state;
+
+        return (
+            <Layout className='layout-dataconnect'>
                 <Content>
-                    <Card className='dataconnect-body' title={
-                        <Row className='dataconnect-tools' type='flex' justify='space-between'>
+                    <Card title={
+                        <Row className='tools' type='flex' justify='space-between'>
                             <Col style={{ display: 'flex' }}>
                             </Col>
                             <Col className='search'>
-                                <Col>
+                                <Col style={{ padding: '0 5px' }}>
                                     <Search
-                                        value={dataConnect.filterLabel}
                                         placeholder="请输入关键字"
+                                        value={dataConnect.filterLabel}
                                         onChange={e => {
                                             dispatch({ type: 'dataConnect/setFilterLabel', label: e.target.value });
                                         }}
                                     />
                                 </Col>
-                                <Col>
+                                <Col >
                                     <Button onClick={() => {
                                         dispatch({ type: 'dataConnect/setNewModel', model: { dbType: 'oracle' } });
                                         dispatch({ type: 'dataConnect/setNewModelFields', fields: [
@@ -210,42 +191,26 @@ class DataConnect extends React.Component {
                             </Col>
                         </Row>
                     }>
-                        <Table
-                            className='dataconnect-table'
-                            columns={dataConnectColumns}
-                            dataSource={
-                                this.onSort(
-                                    this.onSearch(dataConnect.list, dataConnect.filterLabel)
-                                )
-                            }
-                            size='small'
-                            scroll={{x: false, y: true}}
-                            pagination={false}
-                            onRow={(record) => {
-                                return {
-                                    onClick: () => {this.setState({ selectedRecord:  record})}
-                                }
-                            }}
-                        />
-                        {visibleDeleteBox && <DeleteBox
-                            visibleBox={visibleDeleteBox}
-                            text={<div><span>确定要删除数据连接【{selectedRecord.name}】吗?</span></div>}
-                            hideBox={() => {
-                                this.setState({
-                                    visibleDeleteBox: false
-                                })
-                            }}
-                            okHandler={() =>{
-                                dispatch({ type: 'dataConnect/remoteDelete', code: selectedRecord.code })
-                            }} 
-                        />}
-                        <DataConnectBox />
+                        <div className='body'>
+                            { this.generateCard() }
+                        </div>
                     </Card>
                 </Content>
+                {visibleDeleteBox && <DeleteBox
+                    visibleBox={visibleDeleteBox}
+                    text={<div><span>确定要删除数据连接【{selected.name}】吗?</span></div>}
+                    hideBox={() => {
+                        this.setState({
+                            visibleDeleteBox: false
+                        })
+                    }}
+                    okHandler={() =>{
+                        dispatch({ type: 'dataConnect/remoteDelete', code: selected.code })
+                    }} 
+                />}
+                <DataConnectBox />
             </Layout>
         )
-
-
     }
 }
 

+ 57 - 87
src/components/dataConnect/list.less

@@ -1,95 +1,63 @@
-.dataconnect-view {
-    .dataconnect-body {
-        padding: 0;
-        .ant-card-head {
-            padding: 0 10px;
-            .dataconnect-tools {
-                flex: 1;
-                .anticon-bars {
-                    cursor: pointer;
-                    line-height: 1.6;
-                    font-size: 20px;
-                    margin-right: 6px;
-                    &> svg {
-                        height: 100%;
-                    }
-                }
-                .group {
-                    line-height: 2.1;
-                    .ant-breadcrumb-link {
-                        .ant-tag {
-                            margin: 0;
+.layout-dataconnect {
+    &>.ant-layout-content {
+        &>.ant-card {
+            &>.ant-card-head {
+                padding: 0 10px;
+                .tools {
+                    flex: 1;
+                    .search {
+                        display: flex;
+                        > div:first-child {
+                            margin-right: 5px;
                         }
-                    }
-                }
-                .search {
-                    display: flex;
-                    > div:first-child {
-                        margin-right: 5px;
-                    }
+                    } 
                 }
             }
-        }
-    }
-    .ant-card-body {
-        padding: 0;
-        .dataconnect-table{
-            .ant-table {
-                .ant-table-scroll {
-                    .ant-table-header {
-                        overflow: hidden;
-                        table {
-                            thead {
-                                th {
-                                    .ant-table-column-sorter, .ant-table-filter-icon {
-                                        opacity: 0;
-                                    }
-                                    :hover {
-                                        .ant-table-column-sorter, .ant-table-filter-icon {
-                                            opacity: 1;
+            &>.ant-card-body {
+                padding: 16px;
+                display: flex;
+                flex-wrap: wrap;
+                justify-content: center;
+                .dataconnect-card {
+                    width: 160px;
+                    height: 160px;
+                    padding: 0;
+                    margin: 5px;
+                    cursor: pointer;
+                    .ant-card {
+                        height: 100%;
+                        display: flex;
+                        flex-direction: column;
+                        .ant-card-head {
+                            min-height: 32px;
+                            background: #F5F5F5;
+                            padding: 0;
+                            .ant-card-head-wrapper {
+                                height: 100%;
+                                .ant-card-head-title {
+                                    padding: 0 16px;
+                                    .ant-row-flex {
+                                        .label {
+                                            overflow: hidden;
+                                            text-overflow: ellipsis
                                         }
                                     }
                                 }
-                                .column-filtered {
-                                    .ant-table-filter-icon {
-                                        opacity: 1;
-                                        color: red;
-                                    }
-                                }
                             }
                         }
-                    }
-                    .ant-table-body {
-                        margin-top: 17px;
-                        overflow-y: auto !important;
-                        table {
-                            padding: 0;
-                            .ant-table-row {
-                                td {
-                                    padding: 8px;
-                                    .dataconnect-name {
-                                        display: flex;
-                                        .dataconnect-type {
-                                            margin-right: 5px;
-                                        }
-                                    }
-                                    .dataconnect-tag {
-                                        margin: 2px;
-                                        cursor: default;
-                                    }
-                                    .ant-dropdown-trigger {
-                                        font-size: 18px;
-                                        cursor: pointer;
-                                    }
-                                }
-                                .action-col {
-                                    display: flex;
-                                    .operation {
-                                        cursor: pointer;
-                                    }
-                                    .operation:hover {
-                                        color: #40a9ff;
-                                    }
+                        .ant-card-body {
+                            padding: 10px;
+                            height: 100%;
+                            display: flex;
+                            width: 100%;
+                            flex-direction: column;
+                            justify-content: center;
+                            .content {
+                                display: flex;
+                                flex-direction: column;
+                                justify-content: space-between;
+                                height: 100%;
+                                .bottom {
                                 }
                             }
                         }
@@ -154,10 +122,8 @@
 .tree-group li.drag-over > span[draggable] {
     opacity: .5;
 }
-
-.operationmenu {
+.menu-operation {
     padding: 0;
-    width: 120px;
     .ant-dropdown-menu-item {
         .anticon {
             margin-right: 6px;
@@ -171,4 +137,8 @@
             display: flex;
         }
     }
+}
+.ant-tooltip-inner {
+    background-color: rgba(255, 255, 255, .9);
+    color: #666
 }

+ 44 - 13
src/components/dataSourceDetail/dataConnectBox.jsx

@@ -57,11 +57,11 @@ class DataConnectBox extends React.Component {
                                 {/* {dataConnect.newOne.invalid !== undefined ? (dataConnect.newOne.invalid ? <span style={{ color: '#F5222D' }}>{dataConnect.newOne.invalidText}</span> : <span style={{ color: '#52C41A' }}>测试通过</span>) : ''} */}
                             </Col>
                             <Col span={12}>
-                                <Button disabled={dataConnect.newOne.validating || dataConnect.newOne.saving} onClick={() => dispatch({ type:'dataConnect/remoteValidate'})}>
+                                {/* <Button disabled={dataConnect.newOne.validating || dataConnect.newOne.saving} onClick={() => dispatch({ type:'dataConnect/remoteValidate'})}>
                                 {dataConnect.newOne.validating ? (<Icon type='loading' />) : ''}{dataConnect.newOne.validating ? '测试中' : '测试'}
-                                </Button>
-                                <Button disabled={dataConnect.newOne.validating || dataConnect.newOne.saving} onClick={() => dispatch({ type:'dataConnect/resetNewModel'})}>清空</Button>
-                                {/* <Button onClick={() => {this.hideBox()}}>取 消</Button> */}
+                                </Button> */}
+                                <Button type='danger' disabled={dataConnect.newOne.validating || dataConnect.newOne.saving} onClick={() => dispatch({ type:'dataConnect/resetNewModel'})}>清空</Button>
+                                <Button onClick={() => {this.hideBox()}}>取 消</Button>
                                 <Button className={dataConnect.newOne.validating ? 'ant-btn-loading' : ''} type="primary" disabled={dataConnect.newOne.validating || dataConnect.newOne.saving} onClick={() => {this.okHandler()}}>
                                     {dataConnect.newOne.saving ? (<Icon type='loading' />) : ''}
                                     {dataConnect.newOne.saving ? '校验中' : '保存'}
@@ -75,8 +75,8 @@ class DataConnectBox extends React.Component {
                     <FormItem
                         label='连接名'
                         {...formItemLayout}
-                        validateStatus={dataConnect.validInfo.name ? dataConnect.validInfo.name.validateStatus : ''}
-                        help={dataConnect.validInfo.name ? dataConnect.validInfo.name.help : ''}
+                        validateStatus={dataConnect.newOne.name ? 'success' : 'error'}
+                        help={dataConnect.newOne.name ? '' : '连接名不能为空'}
                     >
                         <Input
                             disabled={disabled}
@@ -86,7 +86,10 @@ class DataConnectBox extends React.Component {
                         >
                         </Input>
                     </FormItem>
-                    <FormItem label='数据库类型' {...formItemLayout}>
+                    <FormItem label='数据库类型' {...formItemLayout}
+                        validateStatus={dataConnect.newOne.dbType ? 'success' : 'error'}
+                        help={dataConnect.newOne.dbType ? '' : '数据库类型不能为空'}
+                    >
                         <Select
                             disabled={true}
                             placeholder="选择数据库类型"
@@ -115,7 +118,10 @@ class DataConnectBox extends React.Component {
                             <FormItem label='数据库地址' {...{
                                 labelCol: { span: 5 },
                                 wrapperCol: { span: 19 }
-                            }}>
+                            }}
+                            validateStatus={dataConnect.newOne.address ? 'success' : 'error'}
+                            help={dataConnect.newOne.address ? '' : '数据库地址不能为空'}
+                            >
                                 <Input
                                     disabled={disabled}
                                     placeholder="格式:192.168.1.1"
@@ -130,7 +136,10 @@ class DataConnectBox extends React.Component {
                             <FormItem className='input-port' label='端口' {...{
                                 labelCol: { span: 12 },
                                 wrapperCol: { span: 12 }
-                            }}>
+                            }}
+                            validateStatus={dataConnect.newOne.port ? 'success' : 'error'}
+                            help={dataConnect.newOne.port ? '' : '端口不能为空'}
+                            >
                                 <InputNumber
                                     disabled={disabled}
                                     value={dataConnect.newOne.port}
@@ -141,7 +150,10 @@ class DataConnectBox extends React.Component {
                             </FormItem>
                         </Col>
                     </Row>
-                    <FormItem label='数据库名(SID)' {...formItemLayout}>
+                    <FormItem label='数据库名(SID)' {...formItemLayout}
+                        validateStatus={dataConnect.newOne.dbName ? 'success' : 'error'}
+                        help={dataConnect.newOne.dbName ? '' : '数据库名不能为空'}
+                    >
                         <Input
                             disabled={disabled}
                             value={dataConnect.newOne.dbName}
@@ -155,7 +167,10 @@ class DataConnectBox extends React.Component {
                             <FormItem label='用户名' {...{
                                 labelCol: { span: 8 },
                                 wrapperCol: { span: 16 }
-                            }}>
+                            }}
+                            validateStatus={dataConnect.newOne.userName ? 'success' : 'error'}
+                            help={dataConnect.newOne.userName ? '' : '用户名不能为空'}
+                            >
                                 <Input
                                     disabled={disabled}
                                     value={dataConnect.newOne.userName}
@@ -165,11 +180,14 @@ class DataConnectBox extends React.Component {
                                 />
                             </FormItem>
                         </Col>
-                        <Col span={12}>
+                        <Col span={8}>
                             <FormItem label='密码' {...{
                                 labelCol: { span: 8 },
                                 wrapperCol: { span: 16 }
-                            }}>
+                            }}
+                            // validateStatus={dataConnect.newOne.userName ? 'success' : 'error'}
+                            // help={dataConnect.newOne.userName ? '' : '用户名不能为空'}
+                            >
                                 <Input
                                     disabled={disabled}
                                     className='password'
@@ -181,6 +199,19 @@ class DataConnectBox extends React.Component {
                                 />
                             </FormItem>
                         </Col>
+                        <Col span={4}>
+                            <FormItem>
+                                <Button
+                                    style={{ marginLeft: '10px' }}
+                                    disabled={dataConnect.newOne.validating || dataConnect.newOne.saving}
+                                    onClick={() => dispatch({ type:'dataConnect/remoteValidate'})}
+                                    className={dataConnect.newOne.invalid === undefined ? 'valid-default' : ( dataConnect.newOne.invalid ? 'valid-failure' : 'valid-success' )}
+                                >
+                                    {dataConnect.newOne.validating ? (<Icon type='loading' />) : ( dataConnect.newOne.invalid === undefined ? <Icon type='question-circle' /> : ( dataConnect.newOne.invalid ? <Icon type="exclamation-circle" /> : <Icon type="check-circle" /> ) ) }
+                                    {dataConnect.newOne.invalid === undefined ? '测试' : ( dataConnect.newOne.invalid ? '失败' : '成功' )}
+                                </Button>
+                            </FormItem>
+                        </Col>
                     </Row>
                     <FormItem className='textarea-desc' label='说明' {...formItemLayout}>
                         <Input.TextArea

+ 15 - 4
src/components/dataSourceDetail/dataConnectBox.less

@@ -10,9 +10,20 @@
         .textarea-desc {
             margin-top: 4px;
         }
-    }
-    .validatemessage {
-        text-align: left;
-        color: #F5222D;
+        .valid-default {
+            color: #fff;
+            background-color: #faad14;
+            border-color: #faad14;
+        }
+        .valid-failure {
+            color: #fff;
+            background-color: #ff4d4f;
+            border-color: #ff4d4f;
+        }
+        .valid-success {
+            color: #fff;
+            background-color: #52C41A;
+            border-color: #52C41A;
+        }
     }
 }

+ 2 - 5
src/models/chart.js

@@ -355,7 +355,7 @@ export default {
             try {
                 const res = yield call(service.fetch, {
                     url: URLS.CHART_DELETE,
-                    body: [code]
+                    body: code
                 });
                 console.log('删除图表', code, res);
                 if(!res.err && res.data.code > 0) {
@@ -530,20 +530,17 @@ export default {
 
                 yield put({ type: 'remoteModifyGroups', groups: bgroups });
 
-                console.log('删除图表分组', group.code);
                 const res = yield call(service.fetch, {
                     url: URLS.GROUP_CHART_DELETE,
-                    body: [group.code]
+                    body: group.code
                 });
                 
-                console.log('删除图表分组', group.code, res);
                 if(!res.err && res.data.code > 0) {
                     yield put({ type: 'deleteGroup', group});
                 }else {
                     message.error('删除分组失败: ' + (res.err || res.data.msg));
                 }
             }catch(e) {
-                console.log(e);
                 message.error('删除分组失败: ' + e);
             }
         },

+ 1 - 1
src/models/chartPolicy.js

@@ -188,7 +188,7 @@ export default {
         *remoteDelete(action, { put, call, select }) {
             const chartPolicy = yield select(state => state.present.chartPolicy);
             const { code } = action;
-            const body = [code];
+            const body = code;
             try {
                 const res = yield call(service.fetch, {
                     url: URLS.CHART_POLICY_DELETE,

+ 1 - 3
src/models/dashboard.js

@@ -270,9 +270,8 @@ export default {
             try {
                 const res = yield call(service.fetch, {
                     url: URLS.DASHBOARD_DELETE,
-                    body: [code]
+                    body: code
                 });
-                console.log('删除看板', [code], res);
                 if(!res.err && res.data.code > 0) {
                     for(let i = 0; i < list.length; i++) {
                         if(list[i].code === code) {
@@ -286,7 +285,6 @@ export default {
                     message.error('删除失败: ' + (res.err || res.data.msg));
                 }
             }catch(e) {
-                console.log(e);
                 message.error('删除失败: ' + e);
             }
         },

+ 15 - 7
src/models/dataConnect.js

@@ -10,7 +10,7 @@ export default {
             newOne: {},
             filterLabel: '',
             validInfo: {}
-        }
+        },
     },
     reducers: {
         list(state, action) {
@@ -63,7 +63,9 @@ export default {
             let newOne = state.newOne;
             let validInfo = state.validInfo;
             newOne[name] = value;
-            delete newOne.invalid;
+            if(['dbType', 'address', 'port', 'dbName', 'userName', 'password'].indexOf(name) !== -1) {
+                newOne.invalid = undefined;
+            }
             
             return Object.assign({}, state, { newOne, validInfo });
         },
@@ -71,14 +73,21 @@ export default {
             const { fields } = action;
             let newOne = state.newOne;
             for(let i = 0; i < fields.length; i++) {
-                newOne[fields[i]['name']] = fields[i]['value'];
+                let n = fields[i]['name'];
+                let v = fields[i]['value'];
+                newOne[n] = v;
+
+                // 如果改动了需要重新校验的项
+                if(['dbType', 'address', 'port', 'dbName', 'userName', 'password'].indexOf(n) !== -1) {
+                    newOne.invalid = undefined;
+                }
             }
-            delete newOne.invalid;
             return Object.assign({}, state, {newOne});
         },
         setNewModel(state, action) {
             const { model } = action;
             let newOne = Object.assign({}, model);
+            newOne.invalid = undefined;
             return Object.assign({}, state, {newOne});
         },
         resetNewModel(state, action) {
@@ -92,6 +101,7 @@ export default {
             delete newOne.password;
             delete newOne.description;
             delete newOne.demo;
+            newOne.invalid = undefined;
             return Object.assign({}, state, {newOne});
         },
         setNewModelInvalid(state, action) {
@@ -278,9 +288,8 @@ export default {
             try {
                 const res = yield call(service.fetch, {
                     url: URLS.DATACONNECT_DELETE,
-                    body: [code]
+                    body: code
                 });
-                console.log(code, res);
                 if(!res.err && res.data.code > 0) {
                     for(let i = 0; i < list.length; i++) {
                         if((list[i].code+'') === (code+'')) {
@@ -294,7 +303,6 @@ export default {
                     message.error('删除失败: ' + (res.err || res.data.msg));
                 }
             }catch(e) {
-                console.log(e);
                 message.error('删除失败: ' + e);
             }
         }

+ 2 - 10
src/models/dataSource.js

@@ -293,7 +293,7 @@ export default {
             try {
                 const res = yield call(service.fetch, {
                     url: URLS.DATASOURCE_DELETE,
-                    body: [code]
+                    body: code
                 });
                 if(!res.err && res.data.code > 0) {
                     for(let i = 0; i < list.length; i++) {
@@ -454,19 +454,16 @@ export default {
                     groupName: group.label,
                     groupIndex: group.index,
                 }
-                console.log('修改数据源分组', body);
                 const res = yield call(service.fetch, {
                     url: URLS.GROUP_DATASOURCE_UPDATE,
                     body: body
                 });
-                console.log('修改数据源分组', body, res);
                 if(!res.err && res.data.code > 0) {
                     yield put({ type: 'setGroupDirty', dirty: false });
                 }else {
                     message.error('修改分组失败: ' + (res.err || res.data.msg));
                 }
             }catch(e) {
-                console.log(e);
                 message.error('修改分组失败: ' + e);
             }
         },
@@ -485,20 +482,17 @@ export default {
                         fatherId: g.pcode,
                     }
                 });
-                console.log('批量修改数据源分组', body);
                 const res = yield call(service.fetch, {
                     url: URLS.GROUP_DATASOURCE_LIST_UPDATE,
                     body: body
                 });
                 
-                console.log('批量修改数据源分组', body, res);
                 if(!res.err && res.data.code > 0) {
                     yield put({ type: 'modifyGroups', groups: groups });
                 }else {
                     message.error('修改分组失败: ' + (res.err || res.data.msg));
                 }
             }catch(e) {
-                console.log(e);
                 message.error('修改分组失败: ' + e);
             }
         },
@@ -518,17 +512,15 @@ export default {
 
                 const res = yield call(service.fetch, {
                     url: URLS.GROUP_DATASOURCE_DELETE,
-                    body: [group.code]
+                    body: group.code
                 });
                 
-                console.log('删除分组', group.code, res);
                 if(!res.err && res.data.code > 0) {
                     yield put({ type: 'deleteGroup', group});
                 }else {
                     message.error('删除分组失败: ' + (res.err || res.data.msg));
                 }
             }catch(e) {
-                console.log(e);
                 message.error('删除分组失败: ' + e);
             }
         },

+ 1 - 3
src/models/dataSourcePolicy.js

@@ -180,13 +180,12 @@ export default {
         *remoteDelete(action, { put, call, select }) {
             const dataSourcePolicy = yield select(state => state.present.dataSourcePolicy);
             const { code } = action;
-            const body = [code];
+            const body = code;
             try {
                 const res = yield call(service.fetch, {
                     url: URLS.DATASOURCE_POLICY_DELETE,
                     body
                 });
-                console.log('删除策略', body, res);
                 if(!res.err && res.data.code > 0) {
                     let { list } = dataSourcePolicy;
                     for(let i = 0; i < list.length; i++) {
@@ -200,7 +199,6 @@ export default {
                     message.error('删除失败: ' + (res.err || res.data.msg));
                 }
             }catch(e) {
-                console.log(body, e);
                 message.error('删除失败: ' + e);
             }
         },

+ 1 - 1
src/models/userGroup.js

@@ -183,7 +183,7 @@ export default {
             try {
                 const res = yield call(service.fetch, {
                     url: URLS.USERGROUP_DELETE,
-                    body: [group.code]
+                    body: group.code
                 });
                 if(!res.err && res.data.code > 0) {
                     yield put({ type: 'delete', group });