Jelajahi Sumber

大量样式细节调整

zhuth 6 tahun lalu
induk
melakukan
9418b918ee
44 mengubah file dengan 770 tambahan dan 466 penghapusan
  1. 2 2
      src/components/chart/list.jsx
  2. 8 2
      src/components/chartDesigner/header.less
  3. 2 2
      src/components/common/cusIcon/index.jsx
  4. 1 1
      src/components/common/groupManageMentBox/box.jsx
  5. 1 1
      src/components/dashboard/list.jsx
  6. 4 3
      src/components/dashboard/menu.jsx
  7. 1 0
      src/components/dashboard/menu.less
  8. 82 27
      src/components/dashboardDesigner/configSider.jsx
  9. 11 11
      src/components/dashboardDesigner/configSider.less
  10. 4 4
      src/components/dashboardDesigner/header.jsx
  11. 1 0
      src/components/dataConnect/list.jsx
  12. 2 2
      src/components/dataSource/list.jsx
  13. 2 2
      src/components/dataSourceDetail/accessConfig.jsx
  14. 41 31
      src/components/dataSourceDetail/accessConfig.less
  15. 4 7
      src/components/dataSourceDetail/baseConfig.jsx
  16. 8 0
      src/components/dataSourceDetail/baseConfig.less
  17. 7 6
      src/components/dataSourceDetail/columnConfig.jsx
  18. 49 0
      src/components/dataSourceDetail/columnConfig.less
  19. 19 124
      src/components/dataSourceDetail/content.jsx
  20. 26 176
      src/components/dataSourceDetail/content.less
  21. 105 0
      src/components/dataSourceDetail/contentCreate.jsx
  22. 69 0
      src/components/dataSourceDetail/contentCreate.less
  23. 3 1
      src/components/dataSourceDetail/dataConnectBox.jsx
  24. 7 2
      src/components/dataSourceDetail/dataConnectConfig.jsx
  25. 91 0
      src/components/dataSourceDetail/dataConnectConfig.less
  26. 10 17
      src/components/dataSourceDetail/header.jsx
  27. 13 7
      src/components/dataSourceDetail/header.less
  28. 62 0
      src/components/dataSourceDetail/headerCreate.jsx
  29. 8 0
      src/components/dataSourceDetail/headerCreate.less
  30. 6 4
      src/components/dataSourceDetail/layout.jsx
  31. 0 3
      src/components/dataSourceDetail/layout.less
  32. 9 1
      src/components/dataSourceDetail/otherConfig.jsx
  33. 9 0
      src/components/dataSourceDetail/otherConfig.less
  34. 3 2
      src/components/homePage/collection.jsx
  35. 3 1
      src/components/homePage/collection.less
  36. 4 2
      src/components/logs/logs.jsx
  37. 4 0
      src/models/chart.js
  38. 3 0
      src/models/dashboard.js
  39. 1 0
      src/models/dataSourceDetail.js
  40. 16 0
      src/themes/default/chart.less
  41. 27 7
      src/themes/default/dashboarddesigner.less
  42. 18 0
      src/themes/default/datasource.less
  43. 24 18
      src/themes/default/datasourcedetail.less
  44. TEMPAT SAMPAH
      static/images/uas.png

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

@@ -126,7 +126,7 @@ class ChartList extends React.Component {
                                 dispatch({ type: 'chart/setCurrentGroup', group });
                             }}
                         >
-                            <Icon style={{ marginLeft: '8px', fontSize: '12px' }} type="down" />
+                            <Icon style={{ marginLeft: '2px', fontSize: '12px' }} type="down" />
                         </GroupSelector>
                     </Tag>
                 </Breadcrumb.Item>
@@ -346,7 +346,7 @@ class ChartList extends React.Component {
         return (
             <Layout className='layout-chart'>
                 <Content>
-                    <Card title={
+                    <Card bordered={false} title={
                         <Row className='tools' type='flex' justify='space-between'>
                             <Col style={{ display: 'flex', width: 'calc(100% - 324px)', overflow: 'hidden' }}>
                                 <Checkbox style={{ marginTop: '4px' }} value={noGroup} onChange={(e) => {

+ 8 - 2
src/components/chartDesigner/header.less

@@ -17,12 +17,18 @@
             width: 300px;
         }
         .input-title {
+            text-align: center;
+            font-size: 16px;
+            border: none;
+            border-bottom-right-radius: @border-radius-base;
+            border-top-right-radius: @border-radius-base;
+            background-color: transparent;
             input {
                 text-align: center;
                 font-size: 16px;
                 border: none;
-                border-bottom-right-radius: 4px;
-                border-top-right-radius: 4px;
+                border-bottom-right-radius: @border-radius-base;
+                border-top-right-radius: @border-radius-base;
                 background-color: transparent;
             }
         }

+ 2 - 2
src/components/common/cusIcon/index.jsx

@@ -1,6 +1,6 @@
 import { Icon } from 'antd'
-import scriptUrl from '../../../../static/iconfont/iconfont.js'
+// import scriptUrl from '../../../../static/iconfont/iconfont.js'
  
 export default Icon.createFromIconfontCN({
-    scriptUrl
+    scriptUrl: '//at.alicdn.com/t/font_755957_d9ot28eybfd.js'
 });

+ 1 - 1
src/components/common/groupManageMentBox/box.jsx

@@ -279,7 +279,7 @@ class GroupBox extends React.Component {
         let treeData = arrayToTree(groupData, '-1', 'code', 'pcode', 'children');
         return <Modal
             className='groupmanagement-box'
-            title={'分组管理'}
+            title={'分组维护'}
             visible={visibleBox}
             onOk={() => {
                 okHandler(aGroups, mGroups, dGroups);

+ 1 - 1
src/components/dashboard/list.jsx

@@ -346,7 +346,7 @@ class DashboardList extends React.Component {
         return (
             <Layout className='dashboard-view'>
                 <Content>
-                    <Card className="dashboard-body" title={
+                    <Card bordered={false} className="dashboard-body" title={
                         <Row className='dashboard-tools' type='flex' justify='space-between'>
                             <Col className='menus' style={{ display: 'flex' }}>
                                 {/* { this.generateCurrentMenu() } */}

+ 4 - 3
src/components/dashboard/menu.jsx

@@ -2,6 +2,7 @@ import React from 'react'
 import { connect } from 'dva'
 import { Tree, Input, Button, Icon } from 'antd'
 import DeleteBox from '../common/deleteBox/deleteBox'
+import CusIcon from '../common/cusIcon/index'
 import './menu.less'
 const { TreeNode } = Tree
 const { Search } = Input
@@ -95,7 +96,7 @@ class DashboardMenu extends React.Component {
             let title = <div className={`node-title node-${t.type}`}>
                 <span>{ (t.code !== editingKey) ?
                    ( !!regLabel && (searchMenu || t.type === 'dashboard') ? (<div className='label'> <span title={t.name} style={{ fontWeight: t.type === 'dashboard' ? 'bold' : 'normal' }}>
-                        { t.type === 'dashboard' && <Icon style={{ marginRight: '8px' }} type="pushpin" /> }
+                        { t.type === 'dashboard' && <CusIcon style={{ marginRight: '8px' }} type="bi-dashboard" /> }
                         { (t.name || '').split(new RegExp(`(${regLabel})`, 'i')).map((fragment, i) => {
                             return (
                                 fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'), '\\$1') === regLabel.toLowerCase() ?
@@ -124,7 +125,7 @@ class DashboardMenu extends React.Component {
                         }
                     }}>
                         <span title={t.name} style={{ fontWeight: t.type === 'dashboard' ? 'bold' : 'normal' }}>
-                            { t.type === 'dashboard' && <Icon style={{ marginRight: '8px' }} type="pushpin" /> }
+                            { t.type === 'dashboard' && <CusIcon style={{ marginRight: '8px' }} type="bi-dashboard" /> }
                             { t.name }
                         </span>
                     </div> ) : ( <div className='input'>
@@ -321,7 +322,7 @@ class DashboardMenu extends React.Component {
                                 if(typeof propsOnExpand === 'function') {
                                     propsOnExpand(expandedMenus);
                                 }
-                            }}><span>报表目录</span></div></span>
+                            }}><CusIcon style={{ marginRight: '8px' }} type="bi-compass" /><span>报表目录</span></div></span>
                             {editable && <div className='tools'>
                                 <Icon type='plus' onClick={() => {
                                     this.onAddClick({ code: '-1', name: '报表目录', pcode: '-1', childrenCount: menuTree.length })

+ 1 - 0
src/components/dashboard/menu.less

@@ -36,6 +36,7 @@
                 &>span {
                     width: ~'calc(100% - 62px)';
                     .label {
+                        font-size: 16px;
                         overflow: hidden;
                         text-overflow: ellipsis;
                         user-select:none;

+ 82 - 27
src/components/dashboardDesigner/configSider.jsx

@@ -1,8 +1,9 @@
 import React from 'react'
 import { connect } from 'dva'
-import { Form, Input, Divider, Icon, Tooltip, Button } from 'antd'
+import { Form, Input, Divider, Cascader, Icon, Tooltip, Button } from 'antd'
 import ChooseChartBox from './chooseChartBox'
 import CusFilterBox from './cusFilterBox'
+import CusIcon from '../common/cusIcon/index'
 import copy from 'copy-to-clipboard'
 import './configSider.less'
 const FormItem = Form.Item
@@ -19,6 +20,10 @@ class ConfigSider extends React.Component {
         };
     }
 
+    componentDidMount() {
+        this.props.dispatch({ type: 'dashboard/remoteMenuTree' })
+    }
+
     showChooseChartBox = (o) => {
         this.setState({
             visibleChooseChartBox: true
@@ -48,42 +53,92 @@ class ConfigSider extends React.Component {
         const { visibleChooseChartBox } = this.state;
 
         return (<div className='view-types'>
-            <Tooltip placement='bottom' title="图表">
-                <div className="view-type-item">
-                    <Icon className='viewtype-icon' type="area-chart" theme="outlined" onClick={(item) => {
-                        this.showChooseChartBox("create");
-                    }}/>
-                </div>
-            </Tooltip >
-            <Tooltip placement='bottom' title="富文本">
-                <div className="view-type-item">
-                    <Icon className='viewtype-icon' type="book" theme="outlined" onClick={() => {
-                        dispatch({ type: 'dashboardDesigner/addRichText' });
-                        setTimeout(() => {
-                            // 滚动到最底部
-                            let viewScrollContent = document.querySelector('.viewlayout');
-                            viewScrollContent.scrollTo(0, viewScrollContent.scrollHeight - viewScrollContent.clientHeight);
-                        }, 500);
-                    }}/>
-                </div>
-            </Tooltip>
+            <div  className='view-type-item'onClick={() => {
+                this.showChooseChartBox("create");
+            }}>
+                <Icon type='area-chart'/>
+                <span>图表选择</span>
+            </div>
+            <div className='view-type-item' onClick={() => {
+                dispatch({ type: 'dashboardDesigner/addRichText' });
+                setTimeout(() => {
+                    // 滚动到最底部
+                    let viewScrollContent = document.querySelector('.viewlayout');
+                    viewScrollContent.scrollTo(0, viewScrollContent.scrollHeight - viewScrollContent.clientHeight);
+                }, 500);
+            }}>
+                <Icon type='book'/>
+                <span>富文本</span>
+            </div>
             {visibleChooseChartBox && <ChooseChartBox visibleBox={visibleChooseChartBox} hideBox={this.hideChooseChartBox} />}
         </div>)
     }
 
+    generateMenuOptions = (menuTree) => {
+        return menuTree.map(t => {
+            t.children = t.children instanceof Array ? t.children : [];
+            return {
+                key: t.code,
+                value: t.code,
+                label: t.name,
+                children: this.generateMenuOptions(t.children)
+            }
+        })
+    }
+
+    getParens = (menu) => {
+        const { menuList } = this.props.dashboard;
+        let pmenus = [menu];
+        let fmenu = menuList.find(l => l.code === menu.pcode);
+        if(fmenu) {
+            pmenus = pmenus.concat(this.getParens(fmenu));
+        }
+        return pmenus;
+    }
+
+    getMenuValue = () => {
+        const { dashboard, dashboardDesigner } = this.props;
+        const { menuCode: key } = dashboardDesigner;
+        let menu = dashboard.menuList.find(m => m.code === key);
+        if(menu) {
+            let menus = this.getParens(menu);
+            let val = menus.reverse().map(m => m.code);
+            return val;
+        }else {
+            return null;
+        }
+    }
+
     render() {
-        const { dashboardDesigner, dispatch } = this.props;
+        const { dashboard, dashboardDesigner, dispatch } = this.props;
         const { visibleCusFilterBox, copyDisabled, copyText } = this.state;
+        const { menuTree, menuList } = dashboard;
 
         return <Form className='form-config' layout={'vertical'}>
-            <Divider>报表制作</Divider>
+            <div className='divider'>报表制作</div>
             {this.generateViewTypes()}
-            <Divider>字段过滤</Divider>
-            <Button className="cus-filter-button" onClick={this.showCusFilterBox}>
+            <div className='divider'>字段过滤</div>
+            <div className="cus-filter-button" onClick={this.showCusFilterBox}>
                 <Icon type='bulb' theme='outlined' />自定义过滤字段
-            </Button>
+            </div>
             {visibleCusFilterBox && <CusFilterBox visibleBox={visibleCusFilterBox} hideBox={this.hideCusFilterBox} />}
-            <Divider>其他设置</Divider>
+            <div className='divider'>其他设置</div>
+            <FormItem label='所属目录'>
+                <Cascader
+                    value={this.getMenuValue()}
+                    allowClear={false}
+                    changeOnSelect={true}
+                    expandTrigger='hover'
+                    placeholder='无'
+                    options={this.generateMenuOptions(menuTree)}
+                    onChange={(value, items) => {
+                        let v = value[value.length - 1];
+                        dispatch({ type: 'dashboardDesigner/setField', name: 'menuCode', value: v });
+                    }}
+                    
+                >
+                </Cascader>
+            </FormItem>
             <FormItem label='备注'>
                 <Input.TextArea
                     autosize={{ minRows: 2, maxRows: 6 }}
@@ -122,4 +177,4 @@ class ConfigSider extends React.Component {
     }
 }
 
-export default connect(({ present: { dashboardDesigner } }) => ({ dashboardDesigner }))(ConfigSider);
+export default connect(({ present: { dashboard, dashboardDesigner } }) => ({ dashboard, dashboardDesigner }))(ConfigSider);

+ 11 - 11
src/components/dashboardDesigner/configSider.less

@@ -1,23 +1,23 @@
 .form-config {
-    .ant-divider {
-        margin: 10px 0;
+    .divider {
+        font-weight: bold;
     }
     .view-types {
         display: flex;
+        flex-direction: column;
         .view-type-item {
-            width: 40px;
-            height: 40px;
-            border: 1px solid #1890ff;
-            border-radius: 4px;
-            margin-left: 8px;
+            cursor: pointer;
+            margin: 8px;
             i {
-                font-size: 32px;
-                color: #1890ff;
-                padding: 4px;
+                margin-right: 8px;
             }
         }
     }
     .cus-filter-button {
-        width: 100%;
+        cursor: pointer;
+        margin: 8px;
+        i {
+            margin-right: 8px;
+        }
     }
 }

+ 4 - 4
src/components/dashboardDesigner/header.jsx

@@ -67,14 +67,14 @@ class Header extends React.Component {
                                 dispatch({ type: 'dashboardDesigner/reset' });
                             }
                         }}>
-                            <Icon type='left' />返回
+                            返回
                         </Button>
                     </Popconfirm>}
                     {!this.isOwner() && <Button onClick={(e) => {
                         dispatch({ type: 'main/goBack', path: '/workshop/chart' });
                         dispatch({ type: 'dashboardDesigner/reset' });
                     }}>
-                        <Icon type='left' />返回
+                        返回
                     </Button>}
                 </div>
                 <div className='header-item toolbar-title'>
@@ -129,12 +129,12 @@ class Header extends React.Component {
                         }else {
                             dispatch({ type: 'dashboard/remoteAdd' });
                         }
-                    }}><Icon type='save' />保存</Button>}
+                    }}>保存</Button>}
                     {this.isOwner() && <Button style={{ marginLeft: '8px' }} onClick={() => {
                         this.setState({
                             visibleDeleteBox: true
                         });
-                    }}><Icon type='delete' />删除</Button>}
+                    }}>删除</Button>}
                     {visibleDeleteBox && <DeleteBox
                         visibleBox={visibleDeleteBox}
                         text={<div><span>确定要删除报表【{dashboardDesigner.name}】吗?</span></div>}

+ 1 - 0
src/components/dataConnect/list.jsx

@@ -140,6 +140,7 @@ class DataConnect extends React.Component {
         }).filter(a => a!==null).map( (l, i) => (
             <CardGrid className='dataconnect-card' key={i}>
                 <Card
+                    bordered={false}
                     hoverable={true}
                     title={
                         <Row type='flex' justify='start'

+ 2 - 2
src/components/dataSource/list.jsx

@@ -167,7 +167,7 @@ class DataSource extends React.Component {
                                 dispatch({ type: 'dataSource/setCurrentGroup', group });
                             }}
                         >
-                            <Icon style={{ marginLeft: '8px', fontSize: '12px' }} type="down" />
+                            <Icon style={{ marginLeft: '2px', fontSize: '12px' }} type="down" />
                         </GroupSelector>
                     </Tag>
                 </Breadcrumb.Item>
@@ -380,7 +380,7 @@ class DataSource extends React.Component {
         return ( 
             <Layout className='layout-datasource'>
                 <Content>
-                    <Card className='datasource-body' title={
+                    <Card bordered={false} className='datasource-body' title={
                         <Row className='datasource-tools' type='flex' justify='space-between'>
                             <Col style={{ display: 'flex', width: 'calc(100% - 324px)', overflow: 'hidden' }}>
                                 <Checkbox style={{ marginTop: '4px' }} value={noGroup} onChange={(e) => {

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

@@ -238,9 +238,9 @@ class DataSourceAccessConfig extends React.Component{
         }];
 
         return (
-            <Layout className='datasource-policy'>
+            <Layout className='policy-config'>
                 <Content>
-                    <Card className='policy-body' title={
+                    <Card bordered={false} className='policy-body' title={
                         <Row className='policy-tools' type='flex' justify='space-between'>
                             <Col className='policy-public' style={{ display: 'flex' }}>
                                 {/* 完全开放数据源<Switch size="small"/> */}

+ 41 - 31
src/components/dataSourceDetail/accessConfig.less

@@ -1,38 +1,48 @@
-.datasource-policy {
-    .ant-card-head {
-        .ant-card-head-title {
-            padding: 8px 0;
-            .policy-tools {
-                .policy-public {
-                    font-size: 14px;
-                    line-height: 2.4;
-                    .ant-switch {
-                        margin-top: 9px;
-                        margin-left: 5px;
+.policy-config {
+    min-height: 100%;
+    background: @content-background-color;
+    padding: 12px;
+    border: 1px solid @border-color-base;
+    >.ant-layout-content {
+        >.policy-body {
+            background: transparent;
+            >.ant-card-head {
+                padding: 0;
+                .ant-card-head-title {
+                    padding: 8px 0;
+                    .policy-tools {
+                        .policy-public {
+                            font-size: 14px;
+                            line-height: 2.4;
+                            .ant-switch {
+                                margin-top: 9px;
+                                margin-left: 5px;
+                            }
+                        }
+                        .policy-tips {
+                            flex-grow: 1;
+                            color: red;
+                            font-size: 14px;
+                            margin-left: 20px;
+                            line-height: 2.4;
+                        }
+                        .search {
+                            .ant-input-search {
+                                width: auto;
+                            }
+                            .add-btn {
+                                margin-left: 5px;
+                            }
+                        }
                     }
                 }
-                .policy-tips {
-                    flex-grow: 1;
-                    color: red;
-                    font-size: 14px;
-                    margin-left: 20px;
-                    line-height: 2.4;
-                }
-                .search {
-                    .ant-input-search {
-                        width: auto;
-                    }
-                    .add-btn {
-                        margin-left: 5px;
-                    }
+            }
+            >.ant-card-body {
+                padding: 0;
+                .filter-tag, .user-tag {
+                    margin: 2px 8px 2px 0;
                 }
             }
         }
     }
-    .ant-card-body {
-        padding: 0;
-        .filter-tag, .user-tag {
-            margin: 2px 8px 2px 0;
-        }
-    }
 }

+ 4 - 7
src/components/dataSourceDetail/baseConfig.jsx

@@ -2,6 +2,7 @@ import React from 'react'
 import { Form, Row, Col, Input, InputNumber, Select, Divider, Cascader } from 'antd'
 import { arrayToTree } from '../../utils/baseUtils'
 import { connect } from 'dva'
+import './baseConfig.less'
 const FormItem = Form.Item
 const SelectOption = Select.Option
 
@@ -82,8 +83,7 @@ class DataSourceBaseConfig extends React.Component {
         }
     
         return (
-            <Form className='form-base' size='small'>
-                <Divider orientation="left">基本配置</Divider>
+            <Form className='base-config' size='small'>
                 <FormItem label='数据源名称' {...formItemLayout}
                     validateStatus={validInfo.name.status}
                     help={validInfo.name.help}
@@ -124,8 +124,6 @@ class DataSourceBaseConfig extends React.Component {
                         </div>
                     ):(
                         <div>
-                            <Divider orientation="left">连接配置</Divider>
-                            {/* <div style={{ textAlign: 'end', color: '#F5222D' }}>*若只修改数据链接,请确认不同数据库取数逻辑一致</div> */}
                             <FormItem label='数据链接' {...formItemLayout}>
                                 <Select
                                     disabled
@@ -145,7 +143,7 @@ class DataSourceBaseConfig extends React.Component {
                                     { this.generateOptions() }
                                 </Select>
                             </FormItem>
-                            <FormItem label='数据库类型' {...formItemLayout}>
+                            {/* <FormItem label='数据库类型' {...formItemLayout}>
                                 <Select
                                     disabled={true}
                                     value={dataSourceDetail.dbType}
@@ -237,11 +235,10 @@ class DataSourceBaseConfig extends React.Component {
                                         />
                                     </FormItem>
                                 </Col>
-                            </Row>
+                            </Row> */}
                         </div>
                     )
                 }
-                <Divider orientation="left">其他配置</Divider>
                 <FormItem label='所属分组' {...formItemLayout}>
                     <Cascader
                         value={getGroup()}

+ 8 - 0
src/components/dataSourceDetail/baseConfig.less

@@ -0,0 +1,8 @@
+.base-config {
+    padding: 20px 80px;
+    min-height: 100%;
+    border: 1px solid @border-color-base;
+    .textarea-desc {
+        margin-top: 4px;
+    }
+}

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

@@ -1,8 +1,9 @@
 import React from 'react'
-import { Form, Input, Button, Select, Table, Checkbox, Divider, Icon, Tooltip } from 'antd'
+import { Form, Input, Button, Select, Table, Checkbox, Divider, Icon, Tooltip, Row } from 'antd'
 import { connect } from 'dva'
 import COLUMN_TYPE from './columnType.json'
-import { Resizable } from 'react-resizable';
+import { Resizable } from 'react-resizable'
+import './columnConfig.less'
 const FormItem = Form.Item
 const SelectOption = Select.Option
 
@@ -282,12 +283,12 @@ class DataSourceColumnConfig extends React.Component {
         }));
 
         return (
-            <div>
+            <div className='column-config'>
                 {
                     dataSourceDetail.type==='database'?(
-                        <div>
+                        <div className='sql-area'>
+                            <Row className='divider'>数据对象</Row>
                             <Form size='small'>
-                                <Divider orientation="left">数据对象</Divider>
                                 <FormItem className='textarea-target'>
                                     <Input.TextArea
                                         disabled={!dataSourceDetail.address}
@@ -363,7 +364,7 @@ class DataSourceColumnConfig extends React.Component {
                                     </Tooltip>
                                 </div>
                             </Form>
-                            <Divider orientation="left">数据列</Divider>
+                            <Row className='divider'>数据列</Row>
                         </div>
                     ):null
                 }

+ 49 - 0
src/components/dataSourceDetail/columnConfig.less

@@ -0,0 +1,49 @@
+.column-config {
+    min-height: 100%;
+    background: @content-background-color;
+    padding: 12px;
+    border: 1px solid @border-color-base;
+    >.sql-area {
+        >.ant-row.divider {
+            font-weight: bold;
+            color: #666666;
+        }
+        >.ant-form {
+            >.buttons {
+                display: flex;
+                justify-content: space-between;
+                >.errormessage {
+                    color: red;
+                }
+            }
+        }
+    }
+    >.table-columnconfig {
+        table {
+            overflow: hidden;
+        }
+        .ant-table-thead > tr > th, .ant-table-tbody > tr > td {
+            padding: 4px 8px;
+        }
+        .column-groupable, .column-bucketizable {
+            text-align: center;
+        }
+        .react-resizable {
+            position: relative;
+        }
+          
+        .react-resizable-handle {
+            position: absolute;
+            width: 12px;
+            padding: 0;
+            height: 100%;
+            bottom: 0;
+            right: -5px;
+            opacity: 0;
+            cursor: col-resize;
+        }
+        .ant-table-bordered .ant-table-thead > tr > th:first-child, .ant-table-bordered .ant-table-tbody > tr > td:first-child {
+            border-left: 2px solid #e8e8e8;
+        }
+    }
+}

+ 19 - 124
src/components/dataSourceDetail/content.jsx

@@ -1,14 +1,11 @@
 import React from 'react'
 import { connect } from 'dva'
-import { Layout, Steps, Button, Tabs, Popconfirm, Icon } from 'antd'
-import DataConnectConfig from './dataConnectConfig'
+import { Layout, Tabs } from 'antd'
 import BaseConfig from './baseConfig'
 import ColumnConfig from './columnConfig'
 import AccessConfig from './accessConfig'
-import OtherConfig from './otherConfig'
 import './content.less'
 const { Content } = Layout
-const Step = Steps.Step
 const TabPane = Tabs.TabPane
 
 class DataSourceDetailContent extends React.Component {
@@ -19,54 +16,17 @@ class DataSourceDetailContent extends React.Component {
             mode: props.params.code && props.params.code==='create'?'create':'modify',
             type: props.params.type,
             code: props.params.code,
-            tab: props.params.tab,
-            // current: ['base', 'column', 'access', 'other'].indexOf(props.match.params.tab)
-            current: ['base', 'column', 'other'].indexOf(props.params.tab),
-            visibleConfirm: false,
+            tab: props.params.tab
         }
     }
 
-    next() {
-        const { type, current } = this.state;
-        this.setState({ current: current + 1 });
-        // let step = ['base', 'column', 'access', 'other'][current + 1];
-        let step = ['base', 'column', 'other'][current + 1];
-        this.props.dispatch({ type: 'main/redirect', path: '/workshop/datasource/' + type + '/create/' + step })
-    }
-
-    prev() {
-        const { type, current } = this.state;
-        this.setState({ current: current - 1 });
-        // let step = ['base', 'column', 'access', 'other'][current - 1];
-        let step = ['base', 'column', 'other'][current - 1];
-        this.props.dispatch({ type: 'main/redirect', path: '/workshop/datasource/' + type + '/create/' + step })
-    }
-
-    handleVisibleChange = (visible) => {
-        this.setState({ visibleConfirm: visible });
-    }
-
     render() {
-        const { dispatch, dataSourceDetail, dataConnect } = this.props;
-        const { type, code, tab, mode, current } = this.state;
-
-        const steps = [{
-            tabName: 'base',
-            title: type === 'database' ? '数据链接配置' : '文件选择',
-            content: <DataConnectConfig mode={mode} />,
-        }, {
-            tabName: 'column',
-            title: '数据列配置',
-            content: <ColumnConfig mode={mode} />,
-        }, {
-            tabName: 'other',
-            title: '完成',
-            content: <OtherConfig mode={mode} />,
-        }];
+        const { dispatch } = this.props;
+        const { type, code, tab, mode } = this.state;
 
         const tabs = [{
             tabName: 'base',
-            title: '属性配置',
+            title: '基本信息',
             content: <BaseConfig mode={mode} />,
         }, {
             tabName: 'column',
@@ -80,85 +40,20 @@ class DataSourceDetailContent extends React.Component {
         return (
             <Layout className='content-datasourcedetail'>
                 <Content className='content'>
-                    {mode === 'create' ? (<div className='datasource-steps-container'>
-                        <div className="steps-action">
-                            <Popconfirm
-                                placement="bottomLeft"
-                                title="确定不保存直接退出吗?"
-                                visible={this.state.visibleConfirm}
-                                onVisibleChange={this.handleVisibleChange}
-                                onConfirm={() => {
-                                    dispatch({ type: 'main/redirect', path: '/workshop/datasource' });
-                                    this.setState({
-                                        visibleConfirm: false
-                                    });
-                                }}
-                                onCancel={() => {
-                                    this.setState({
-                                        visibleConfirm: false
-                                    });
-                                }}
-                                okText="确定"
-                                cancelText="取消"
-                            >
-                                <Button onClick={(e) => {
-                                    if(!dataSourceDetail.dirty) {
-                                        dispatch({ type: 'main/redirect', path: '/workshop/datasource' });
-                                    }
-                                }}>
-                                    <Icon type='left' />返回
-                                </Button>
-                            </Popconfirm>
-                            <div className='steps-buttons'>
-                                {
-                                    current > 0 && (
-                                    <Button onClick={() => this.prev()}>
-                                        上一步
-                                    </Button>)
-                                }
-                                {
-                                    current < steps.length - 1
-                                    && <Button disabled={
-                                        // 第一步:没有选择数据连接或没有上传文件或上传文件有误
-                                        ( current === 0 && ( ( type === 'database' && !dataSourceDetail.connectCode ) || ( type === 'file' && +1 === 2) ) ) ||
-                                        // 第二步:数据列为空或者数据对象有变更
-                                        (current === 1 && (!dataSourceDetail.columns || dataSourceDetail.columns.length === 0 || dataSourceDetail.targetDirty))
-                                    } type="primary" onClick={() => this.next()}>下一步</Button>
-                                }
-                                {
-                                    current === steps.length - 1
-                                    && <Button disabled={
-                                        !dataSourceDetail.name || 
-                                        !dataConnect.selected ||
-                                        (!dataSourceDetail.columns || dataSourceDetail.columns.length === 0)
-                                    } type="primary" onClick={() => {
-                                        dispatch({ type: 'dataSource/remoteAdd' })
-                                    }}>完成</Button>
-                                }
-                            </div>
-                        </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>) : (
-                        <Tabs activeKey={tab} type="card"
-                            onChange={(key) => {
-                                dispatch({ type: 'main/redirect', path: '/workshop/datasource/' + type + '/' + code + '/' + key })
-                                this.setState({
-                                    tab: key,
-                                })
-                            }}
-                        >
-                            {tabs.map((item, index) => {
-                                return <TabPane className='tab-datasource' key={item.tabName} tab={item.title}>
-                                    {item.content}
-                                </TabPane>
-                            })}
-                        </Tabs>
-                    )}
+                    <Tabs activeKey={tab} type="card"
+                        onChange={(key) => {
+                            dispatch({ type: 'main/redirect', path: '/workshop/datasource/' + type + '/' + code + '/' + key })
+                            this.setState({
+                                tab: key,
+                            })
+                        }}
+                    >
+                        {tabs.map((item, index) => {
+                            return <TabPane className='tab-datasource' key={item.tabName} tab={item.title}>
+                                {item.content}
+                            </TabPane>
+                        })}
+                    </Tabs>
                 </Content>
             </Layout>
         )

+ 26 - 176
src/components/dataSourceDetail/content.less

@@ -2,15 +2,11 @@
     height: 100%;
     .content {
         height: 100%;
-        padding: 10px;
-        .datasource-steps-container {
-            height: 100%;
-            display: flex;
-            flex-direction: column;
-        }
+        background: @content-background-color;
         &>.ant-tabs {
             height: 100%;
             padding-top: 40px;
+            overflow: visible;
             &> .ant-tabs-bar {
                 border: none;
                 margin: -40px 0 12px 0;
@@ -21,15 +17,37 @@
                             background: @tab-background;
                             border-color: @tab-background-active;
                             border-radius: 0;
+                            border-left-width: 0;
                             border-right-width: 0;
                             margin: 0;
                             text-align: center;
+                            &:after {
+                                content: ' ';
+                                width: 1px;
+                                height: 50%;
+                                position: absolute;
+                                border-right: 1px solid #2C82BE;
+                                margin-left: 16px;
+                                margin-top: 10%;
+                                z-index: 1;
+                            }
                             &.ant-tabs-tab-active {
                                 color: @tab-color-active;
                                 background: @tab-background-active;
+                                &:after {
+                                    content: none;
+                                }
+                            }
+                            &:first-child {
+                                border-radius: 2px 0 0 2px;
+                                border-left-width: 1px;
                             }
                             &:last-child {
+                                border-radius: 0 2px 2px 0;
                                 border-right-width: 1px;
+                                &:after {
+                                    content: none;
+                                }
                             }
                         }
                     }
@@ -37,177 +55,9 @@
             }
             &>.ant-tabs-content {
                 height: 100%;
-            }
-        }
-        .steps-body {
-            height: 48px;
-            display: flex;
-            align-items: center;
-            border: 1px solid @border-color-base;
-            padding: 0 20%;
-        }
-        .steps-content,.tab-datasource {
-            height: 100%;
-            overflow: auto;
-            .ant-divider {
-                margin: 10px 0;
-            }
-            .ant-form {
-                .buttons {
-                    display: flex;
-                    justify-content: space-between;
-                    margin-top: 10px;
-                    .errormessage {
-                        color: #F5222D;
-                    }
-                    button {
-                        margin-left: 4px;
-                    }
-                }
-            }
-            .form-base {
-                height: 100%;
-                .links {
-                    display: flex;
-                    justify-content: flex-end;
-                    .ant-dropdown-trigger {
-                        cursor: pointer;
-                        margin-right: 4px;
-                        color: rgb(24, 144, 255);
-                        height: 32px;
-                        line-height: 2.2;
-                    }
-                }
-                .ant-input-number > .ant-input-number-handler-wrap {
-                    display: none;
-                }
-                textarea {
-                    margin-top: 4px;
-                }
-                .dataconnect {
-                    height: 100%;
-                    >.ant-layout-content {
-                        >.ant-card {
-                            height: 100%;
-                            display: flex;
-                            flex-direction: column;
-                            >.ant-card-head {
-                                height: 48px;
-                                >.ant-card-head-wrapper {
-                                    height: 48px;
-                                }
-                            }
-                            >.ant-card-body {
-                                background: #f4f4f5;
-                                height: calc(~"100% - 48px");
-                                overflow: auto;
-                                padding: 16px;
-                                .dataconnect-list {
-                                    .dataconnect-card {
-                                        width: 160px;
-                                        height: 160px;
-                                        padding: 0;
-                                        margin: 8px;
-                                        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
-                                                            }
-                                                        }
-                                                    }
-                                                }
-                                                .selected {
-                                                    width: 32px;
-                                                    height: 32px;
-                                                    background-repeat: no-repeat;
-                                                    background-size: 32px;
-                                                    background-image: url(../../../static/images/selected.png);
-                                                    position: absolute;
-                                                    right: 0px;
-                                                    top: 0px;
-                                                    border-top-right-radius: 1px;
-                                                }
-                                            }
-                                            .ant-card-body {
-                                                padding: 10px;
-                                                height: 100%;
-                                                display: flex;
-                                                width: 100%;
-                                                flex-direction: column;
-                                                justify-content: center;
-                                                .content {
-                                                    display: flex;
-                                                    height: 100%;
-                                                    flex-direction: column;
-                                                    justify-content: space-between;
-                                                }
-                                            }
-                                            .ant-card-actions {
-                                                height: 32px;
-                                                background: #fff;
-                                                li {
-                                                    margin: 4px 0;
-                                                }
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            .table-columnconfig {
-                table {
-                    overflow: hidden;
-                }
-                .ant-table-thead > tr > th, .ant-table-tbody > tr > td {
-                    padding: 4px 8px;
-                }
-                .column-groupable, .column-bucketizable {
-                    text-align: center;
-                }
-                .react-resizable {
-                    position: relative;
-                }
-                  
-                .react-resizable-handle {
-                    position: absolute;
-                    width: 12px;
-                    padding: 0;
+                >.tab-datasource {
                     height: 100%;
-                    bottom: 0;
-                    right: -5px;
-                    opacity: 0;
-                    cursor: col-resize;
-                }
-                .ant-table-bordered .ant-table-thead > tr > th:first-child, .ant-table-bordered .ant-table-tbody > tr > td:first-child {
-                    border-left: 2px solid #e8e8e8;
-                }
-            }
-        }
-        .steps-action {
-            height: 50px;
-            width: 100%;
-            display: flex;
-            justify-content: space-between;
-            float: right;
-            .steps-buttons {
-                button {
-                    margin-left: 8px;
+                    overflow: auto;
                 }
             }
         }

+ 105 - 0
src/components/dataSourceDetail/contentCreate.jsx

@@ -0,0 +1,105 @@
+import React from 'react'
+import { connect } from 'dva'
+import { Layout, Steps, Button } from 'antd'
+import DataConnectConfig from './dataConnectConfig'
+import ColumnConfig from './columnConfig'
+import OtherConfig from './otherConfig'
+import './contentCreate.less'
+const { Content } = Layout
+const Step = Steps.Step
+
+class DataSourceCreateContent extends React.Component {
+
+    constructor(props) {
+        super(props);
+        this.state = {
+            mode: props.params.code && props.params.code==='create'?'create':'modify',
+            type: props.params.type,
+            code: props.params.code,
+            tab: props.params.tab,
+            // current: ['base', 'column', 'access', 'other'].indexOf(props.match.params.tab)
+            current: ['base', 'column', 'other'].indexOf(props.params.tab),
+        }
+    }
+
+    next() {
+        const { type, current } = this.state;
+        this.setState({ current: current + 1 });
+        // let step = ['base', 'column', 'access', 'other'][current + 1];
+        let step = ['base', 'column', 'other'][current + 1];
+        this.props.dispatch({ type: 'main/redirect', path: '/workshop/datasource/' + type + '/create/' + step })
+    }
+
+    prev() {
+        const { type, current } = this.state;
+        this.setState({ current: current - 1 });
+        // let step = ['base', 'column', 'access', 'other'][current - 1];
+        let step = ['base', 'column', 'other'][current - 1];
+        this.props.dispatch({ type: 'main/redirect', path: '/workshop/datasource/' + type + '/create/' + step })
+    }
+
+    render() {
+        const { dispatch, dataSourceDetail, dataConnect } = this.props;
+        const { type, mode, current } = this.state;
+
+        const steps = [{
+            tabName: 'base',
+            title: type === 'database' ? '选择数据链接' : '选择文件',
+            content: <DataConnectConfig mode={mode} />,
+        }, {
+            tabName: 'column',
+            title: '数据列配置',
+            content: <ColumnConfig mode={mode} />,
+        }, {
+            tabName: 'other',
+            title: '完成',
+            content: <OtherConfig mode={mode} />,
+        }];
+
+        return (
+            <Layout className='content-datasourcecreate'>
+                <Content className='content'>
+                    <div className='datasource-steps-container'>
+                        <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">
+                            <div className='steps-buttons'>
+                                {
+                                    current > 0 && (
+                                    <Button onClick={() => this.prev()}>
+                                        上一步
+                                    </Button>)
+                                }
+                                {
+                                    current < steps.length - 1
+                                    && <Button disabled={
+                                        // 第一步:没有选择数据连接或没有上传文件或上传文件有误
+                                        ( current === 0 && ( ( type === 'database' && !dataSourceDetail.connectCode ) || ( type === 'file' && +1 === 2) ) ) ||
+                                        // 第二步:数据列为空或者数据对象有变更
+                                        (current === 1 && (!dataSourceDetail.columns || dataSourceDetail.columns.length === 0 || dataSourceDetail.targetDirty))
+                                    } type="primary" onClick={() => this.next()}>下一步</Button>
+                                }
+                                {
+                                    current === steps.length - 1
+                                    && <Button disabled={
+                                        !dataSourceDetail.name || 
+                                        !dataConnect.selected ||
+                                        (!dataSourceDetail.columns || dataSourceDetail.columns.length === 0)
+                                    } type="primary" onClick={() => {
+                                        dispatch({ type: 'dataSource/remoteAdd' })
+                                    }}>完成</Button>
+                                }
+                            </div>
+                        </div>
+                    </div>
+                </Content>
+            </Layout>
+        )
+    }
+}
+
+export default connect(({ present: { dataSourceDetail, dataConnect } }) => ({ dataSourceDetail, dataConnect }))(DataSourceCreateContent);

+ 69 - 0
src/components/dataSourceDetail/contentCreate.less

@@ -0,0 +1,69 @@
+.content-datasourcecreate {
+    height: 100%;
+    >.content {
+        >.datasource-steps-container {
+            height: 100%;
+            display: flex;
+            flex-direction: column;
+            background: @content-background-color;
+            >.steps-body {
+                height: 32px;
+                display: flex;
+                align-items: center;
+                padding: 0 20%;
+                margin-bottom: 12px;
+                background: @content-background-color;
+                >.ant-steps-item {
+                    >.ant-steps-item-icon {
+                        width: 18px;
+                        height: 18px;
+                        line-height: 18px;
+                        margin-top: 4px;
+                        margin-right: 4px;
+                    }
+                    >.ant-steps-item-content {
+                        >.ant-steps-item-title {
+                            color: #B9B9B9;
+                            line-height: 24px;
+                            &:after {
+                                height: 0;
+                                background-color: none;
+                                border-top: 1px dashed #B9B9B9;
+                            }
+                        }
+                    }
+                    &.ant-steps-item-process,&.ant-steps-item-finish {
+                        >.ant-steps-item-icon {
+                            >.ant-steps-icon {
+                                color: @item-active-color;
+                            }
+                            background: @item-active-bg-color;
+                        }
+                        >.ant-steps-item-content > .ant-steps-item-title {
+                            color: @item-active-bg-color;
+                        }
+                    }
+                }
+            }
+            >.steps-content {
+                flex: 1;
+                overflow: auto;
+                padding: 12px;
+                background: #FFFFFF;
+                box-shadow: 0 0 10px 0 rgba(41,54,72,0.10);
+            }
+            >.steps-action {
+                height: 38px;
+                line-height: 50px;
+                width: 100%;
+                display: flex;
+                justify-content: flex-end;
+                .steps-buttons {
+                    button {
+                        margin-left: 8px;
+                    }
+                }
+            }
+        }
+    }
+}

+ 3 - 1
src/components/dataSourceDetail/dataConnectBox.jsx

@@ -126,7 +126,7 @@ class DataConnectBox extends React.Component {
                 destroyOnClose={true}
                 footer={
                     operation === 'view' ? null : (
-                        <Row>
+                        <Row type='flex' justify='space-between'>
                             <Col>
                                 <Button type='danger' disabled={dataConnect.newOne.validating || dataConnect.newOne.saving} onClick={() => {
                                     dispatch({ type:'dataConnect/resetNewModel'});
@@ -134,6 +134,8 @@ class DataConnectBox extends React.Component {
                                         password: '',
                                     });
                                 }}>清空</Button>
+                            </Col>
+                            <Col>
                                 <Button onClick={() => {this.hideBox()}}>取 消</Button>
                                 <Button className={dataConnect.newOne.validating ? 'ant-btn-loading' : ''} type="primary" disabled={dataConnect.newOne.validating || dataConnect.newOne.saving || !this.checkValid()} onClick={() => {this.okHandler()}}>
                                     {dataConnect.newOne.saving ? (<Icon type='loading' />) : ''}

+ 7 - 2
src/components/dataSourceDetail/dataConnectConfig.jsx

@@ -1,8 +1,12 @@
+/**
+ * 新增数据源-选择数据链接
+ */
 import React from 'react'
 import { Layout, Form, Row, Col, Input, Icon, Menu, Dropdown, Divider, Upload, message, Card } from 'antd'
 import { connect } from 'dva'
 import DataConnectBox from './dataConnectBox'
 import EmptyContent from '../common/emptyContent/index'
+import './dataConnectConfig.less'
 const { Content } = Layout
 const CardGrid = Card.Grid
 const UploadDragger = Upload.Dragger
@@ -84,6 +88,7 @@ class DataConnectConfig extends React.Component {
                             dispatch({ type: 'dataConnect/setSelected', selected: l });
                             dispatch({ type: 'dataSourceDetail/setFields', fields: [
                                 { name: 'connectCode', value: l.code },
+                                { name: 'connectName', value: l.name },
                                 { name: 'dbType', value: l.dbType },
                                 { name: 'address', value: l.address },
                                 { name: 'port', value: l.port },
@@ -132,7 +137,7 @@ class DataConnectConfig extends React.Component {
         const { filterLabel } = this.state;
 
         return (
-            <Form className='form-base' size='small'>
+            <Form className='dataconnect-config' size='small'>
                 {
                     dataSourceDetail.type==='file'?(
                         <div>
@@ -185,7 +190,7 @@ class DataConnectConfig extends React.Component {
                     ):(
                         <Layout className='dataconnect'>
                             <Content>
-                                <Card title={
+                                <Card bordered={false} title={
                                     <Row type='flex' justify='end'>
                                         <Search
                                             style={{ width: '200px' }}

+ 91 - 0
src/components/dataSourceDetail/dataConnectConfig.less

@@ -0,0 +1,91 @@
+.dataconnect-config {
+    height: 100%;
+    background: @content-background-color;
+    border: 1px solid @border-color-base;
+    .dataconnect {
+        height: 100%;
+        >.ant-layout-content {
+            >.ant-card {
+                height: 100%;
+                display: flex;
+                flex-direction: column;
+                background: @content-background-color;
+                >.ant-card-head {
+                    height: 48px;
+                    >.ant-card-head-wrapper {
+                        height: 48px;
+                    }
+                }
+                >.ant-card-body {
+                    background: #f4f4f5;
+                    height: calc(~"100% - 48px");
+                    overflow: auto;
+                    padding: 16px;
+                    .dataconnect-list {
+                        .dataconnect-card {
+                            width: 160px;
+                            height: 160px;
+                            padding: 0;
+                            margin: 8px;
+                            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
+                                                }
+                                            }
+                                        }
+                                    }
+                                    .selected {
+                                        width: 32px;
+                                        height: 32px;
+                                        background-repeat: no-repeat;
+                                        background-size: 32px;
+                                        background-image: url(../../../static/images/selected.png);
+                                        position: absolute;
+                                        right: 0px;
+                                        top: 0px;
+                                        border-top-right-radius: 1px;
+                                    }
+                                }
+                                .ant-card-body {
+                                    padding: 10px;
+                                    height: 100%;
+                                    display: flex;
+                                    width: 100%;
+                                    flex-direction: column;
+                                    justify-content: center;
+                                    .content {
+                                        display: flex;
+                                        height: 100%;
+                                        flex-direction: column;
+                                        justify-content: space-between;
+                                    }
+                                }
+                                .ant-card-actions {
+                                    height: 32px;
+                                    background: #fff;
+                                    li {
+                                        margin: 4px 0;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 10 - 17
src/components/dataSourceDetail/header.jsx

@@ -39,8 +39,11 @@ class DataSourceDetailHeader extends React.Component {
 
         return (
             <div className='dataSourcedetail-header'>
-                <div>
-                    {code && code !== 'create' && <Popconfirm
+                <div style={{ maxWidth: '400px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
+                    <span className='title-label' title={name || '未命名'}>{name || '未命名'}</span>
+                </div>
+                <div className='buttons'>
+                    <Popconfirm
                         overlayClassName={`close-popconfirm${this.isValid() ? '' : ' confirm-disabled'}`}
                         placement="bottomLeft"
                         title="离开前保存修改吗?"
@@ -64,26 +67,16 @@ class DataSourceDetailHeader extends React.Component {
                         }}
                         okText="保存"
                         cancelText="不保存"
-                    >
+                        >
                         <Button onClick={(e) => {
                             if(!dataSourceDetail.dirty) {
                                 dispatch({ type: 'main/redirect', path: '/workshop/datasource' });
                             }
                         }}>
-                            <Icon type='left' />返回
+                            返回
                         </Button>
-                    </Popconfirm>}
-                </div>
-                <div style={{ maxWidth: '400px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
-                    <span className='title-label' title={name || '未命名'}>{name || '未命名'}</span>
-                </div>
-                <div className='header-item buttons'>
-                    {!code || code === 'create' && <Button onClick={(e) => {
-                        dispatch({ type: 'main/redirect', path: '/workshop/datasource' });
-                    }}>
-                        <Icon type='left' />返回
-                    </Button>}
-                    {code && code !== 'create' && <Popconfirm
+                    </Popconfirm>
+                    <Popconfirm
                         placement="bottomLeft"
                         title={<span style={{ color: 'red' }}>修改数据源可能会对已创建的图表/报表产生影响,是否确认修改?</span>}
                         visible={this.state.visibleSaveConfirm}
@@ -110,7 +103,7 @@ class DataSourceDetailHeader extends React.Component {
                         }}>
                             保存
                         </Button>
-                    </Popconfirm>}
+                    </Popconfirm>
                 </div>
             </div>
         );

+ 13 - 7
src/components/dataSourceDetail/header.less

@@ -2,17 +2,23 @@
     display: flex;
     justify-content: space-between;
     .title-label {
+        font-weight: bold;
         font-size: 16px;
     }
+    .buttons {
+        button {
+            margin-left: 8px;
+        }
+    }
 }
 .close-popconfirm.confirm-disabled {
     .ant-popover-buttons {
-    button:last-child {
-        cursor: not-allowed;
-        color: rgba(0, 0, 0, 0.25);
-        background-color: #f5f5f5;
-        border-color: #ccc;
-        text-shadow: none;
+        button:last-child {
+            cursor: not-allowed;
+            color: rgba(0, 0, 0, 0.25);
+            background-color: #f5f5f5;
+            border-color: #ccc;
+            text-shadow: none;
+        }
     }
-}
 }

+ 62 - 0
src/components/dataSourceDetail/headerCreate.jsx

@@ -0,0 +1,62 @@
+import React from 'react'
+import { Row, Col, Button, Popconfirm, Icon } from 'antd'
+import { connect } from 'dva'
+import './headerCreate.less'
+
+class DataSourceCreateHeader extends React.Component {
+
+    constructor(props) {
+        super(props);
+        this.state = {
+            visibleConfirm: false,
+        }
+    }
+
+    handleVisibleChange = (visible) => {
+        this.setState({ visibleConfirm: visible });
+    }
+
+    render() {
+        const { dataSourceDetail, dispatch } = this.props;
+        const { visibleConfirm } = this.state;
+
+        return (
+            <div className='header-datasource-create'>
+                <div className='title'>
+                    <span>创建数据源</span>
+                </div>
+                <div>
+                    <Popconfirm
+                        placement="bottomLeft"
+                        title="确定不保存直接退出吗?"
+                        visible={visibleConfirm}
+                        onVisibleChange={this.handleVisibleChange}
+                        onConfirm={() => {
+                            dispatch({ type: 'main/redirect', path: '/workshop/datasource' });
+                            this.setState({
+                                visibleConfirm: false
+                            });
+                        }}
+                        onCancel={() => {
+                            this.setState({
+                                visibleConfirm: false
+                            });
+                        }}
+                        okText="确定"
+                        cancelText="取消"
+                    >
+                        <Button onClick={(e) => {
+                            if(!dataSourceDetail.dirty) {
+                                dispatch({ type: 'main/redirect', path: '/workshop/datasource' });
+                            }
+                        }}>
+                            取消
+                        </Button>
+                    </Popconfirm>
+                </div>
+            </div>
+        );
+    }
+}
+
+export default connect(({ present: { dataSourceDetail } }) => ({ dataSourceDetail }))(DataSourceCreateHeader);

+ 8 - 0
src/components/dataSourceDetail/headerCreate.less

@@ -0,0 +1,8 @@
+.header-datasource-create {
+    display: flex;
+    justify-content: space-between;
+    .title {
+        font-weight: bold;
+        font-size: 16px;
+    }
+}

+ 6 - 4
src/components/dataSourceDetail/layout.jsx

@@ -2,7 +2,9 @@ import React from 'react'
 import { connect } from 'dva'
 import { Icon, Layout, Spin } from 'antd'
 import DataSourceDetailHeader from './header'
+import DataSourceCreateHeader from './headerCreate'
 import DataSourceDetailContent from './content'
+import DataSourceCreateContent from './contentCreate'
 import './layout.less'
 const { Header, Content } = Layout
 
@@ -36,11 +38,11 @@ class DataSourceDetail extends React.Component {
         const { paramsCode: code } = this.state;
 
         return <Layout className='layout-datasourcedetail'>
-            {code !== 'create' && <Header>
-                <DataSourceDetailHeader updateThumbnail={this.updateThumbnail} />
-            </Header>}
+            <Header>
+                {code === 'create' ? <DataSourceCreateHeader /> : <DataSourceDetailHeader />}
+            </Header>
             <Content style={{ height: 0 }}>
-                <DataSourceDetailContent params={this.props.match.params}/>
+                {code === 'create' ? <DataSourceCreateContent params={this.props.match.params}/> : <DataSourceDetailContent params={this.props.match.params}/>}
             </Content>
             <div style={{ display:'none', position: 'absolute', height: '100%', width: '100%', zIndex: '4', background: 'rgba(51,51,51,.1)' }}>
                 <Spin style={{ display: 'inline-block', position: 'absolute', top: '50%', left: '50%', margin: '-10px' }} indicator={<Icon type="loading" style={{ fontSize: 24 }} spin />} />

+ 0 - 3
src/components/dataSourceDetail/layout.less

@@ -6,9 +6,6 @@
         padding: 0 10px;
         height: 40px;
         line-height: 40px;
-        border-width: 1px 0;
-        border-style: solid;
-        border-color: #CCCCCC;
     }
     &>.ant-layout-content {
         flex: 1;

+ 9 - 1
src/components/dataSourceDetail/otherConfig.jsx

@@ -2,6 +2,7 @@ import React from 'react'
 import { Form, Input, Cascader } from 'antd'
 import { connect } from 'dva'
 import { arrayToTree } from '../../utils/baseUtils'
+import './otherConfig.less'
 const FormItem = Form.Item
 
 const OtherConfig = ({ dataSourceDetail, dataSource, dataConnect, dispatch, mode }) => {
@@ -54,13 +55,20 @@ const OtherConfig = ({ dataSourceDetail, dataSource, dataConnect, dispatch, mode
     const treeData = arrayToTree(dataSource.groupList, '-1', 'code', 'pcode', 'children');
 
     return (
-        <Form className='form-base' size='small'>
+        <Form className='other-config' size='small'>
             <FormItem label='数据源名称' {...formItemLayout}>
                 <Input
                     value={dataSourceDetail.name}
                     onChange={(e) => { dispatch({ type: 'dataSourceDetail/setField', name: 'name', value: e.target.value }) }}>
                 </Input>
             </FormItem>
+            <FormItem label='数据链接' {...formItemLayout}>
+                <Input
+                    readOnly
+                    value={dataSourceDetail.connectName}
+                    onChange={(e) => { dispatch({ type: 'dataSourceDetail/setField', name: 'name', value: e.target.value }) }}>
+                </Input>
+            </FormItem>
             <FormItem label='所属分组' {...formItemLayout}>
                 <Cascader
                     value={getGroup()}

+ 9 - 0
src/components/dataSourceDetail/otherConfig.less

@@ -0,0 +1,9 @@
+.other-config {
+    height: 100%;
+    padding: 20px 80px;
+    background: @content-background-color;
+    border: 1px solid @border-color-base;
+    .textarea-desc {
+        margin-top: 4px;
+    }
+}

+ 3 - 2
src/components/homePage/collection.jsx

@@ -1,6 +1,7 @@
 import React from 'react'
 import { connect } from 'dva'
 import { Collapse, Icon } from 'antd'
+import CusIcon from '../common/cusIcon/index'
 import './collection.less'
 
 class Collection extends React.Component {
@@ -34,7 +35,7 @@ class Collection extends React.Component {
                     this.openTab(c);
                 }}>
                     <span style={{ fontWeight: 'bold', cursor: 'pointer' }}>
-                        <Icon style={{ marginRight: '8px' }} type="pushpin" />
+                        <CusIcon style={{ marginRight: '8px' }} type="bi-dashboard" />
                         {
                             <span>
                                 { (c.name || '').split(new RegExp(`(${regLabel})`, 'i')).map((fragment, i) => {
@@ -75,7 +76,7 @@ class Collection extends React.Component {
                 this.setState({
                     activeKey: activeKey.length > 0 ? [] : ['1']
                 });
-            }}>我的收藏</div>} key="1">
+            }}><CusIcon style={{ marginRight: '8px' }} type="bi-compass" />我的收藏</div>} key="1">
                 { this.generateCollectionMenus() }
             </Collapse.Panel>
         </Collapse>

+ 3 - 1
src/components/homePage/collection.less

@@ -3,6 +3,7 @@
     &>.ant-collapse-item {
         border: none;
         &>.ant-collapse-header {
+            font-size: 16px;
             user-select:none;
             padding: 0 16px 0 30px;
             &>.ant-collapse-arrow {
@@ -17,8 +18,9 @@
             &>.ant-collapse-content-box {
                 padding: 8px 16px;
                 &>.item {
+                    font-size: 16px;
                     user-select:none;
-                    padding: 6px 0 5px 0;
+                    padding: 6px 0 5px 31px;
                     display: flex;
                     justify-content: space-between;
                     cursor: pointer;

+ 4 - 2
src/components/logs/logs.jsx

@@ -1,10 +1,11 @@
 import React from 'react'
 import { Layout, Row, Col, Table, Card, Button } from 'antd'
 import { connect } from 'dva'
-import './logs.less'
+import moment from 'moment'
 import ListFilter from '../common/listFilter/index'
 import EllipsisTooltip from '../common/ellipsisTooltip/index'
 import CusIcon from '../common/cusIcon/index'
+import './logs.less'
 const { Content } = Layout
 
 class Logs extends React.Component {
@@ -166,7 +167,8 @@ class Logs extends React.Component {
             title: '操作时间',
             dataIndex: 'date',
             key: 'date',
-            width: 100
+            width: 100,
+            render: text => moment(text).format('YYYY-MM-DD HH:mm:ss')
         }];
 
         return (      

+ 4 - 0
src/models/chart.js

@@ -238,6 +238,10 @@ export default {
                 });
                 if(!res.err && res.data.code > 0) {
                     let resData = res.data.data;
+                    if(!resData) {
+                        message.error('该图表不存在');
+                        return false;
+                    }
                     let chartConfig = JSON.parse(resData.chartConfig || '{ "xAxis": { "column": {}, "granularity": {} }, "yAxis": { "column": {}, "gauge": {} } }');
                     let styleConfig = JSON.parse(resData.style || '{}');
                     let otherConfig = JSON.parse(resData.otherConfig || '{}');

+ 3 - 0
src/models/dashboard.js

@@ -245,6 +245,7 @@ export default {
             const { hideMessage } = action
             try {
                 const dashboardDesigner = yield select(state => state.present.dashboardDesigner);
+                const dashboard = yield select(state => state.present.dashboard);
                 const { code, name, items, description, relationColumns, filters, chartCodes, shareCode } = dashboardDesigner;
                 let body = {
                     id: code,
@@ -263,6 +264,8 @@ export default {
                 if(!res.err && res.data.code > 0) {
                     yield put({ type: 'fetchList', mandatory: true });
                     yield put({ type: 'dashboardDesigner/silentSetField', name: 'dirty', value: false });
+                    let menu = dashboard.menuList.find(l => l.code === dashboardDesigner.menuCode);
+                    yield put({ type: 'remoteSetMenu', dashboard: { code }, menu })
                     !hideMessage && message.success('保存成功');
                 }else {
                     !hideMessage && message.error('保存失败: ' + (res.err || res.data.msg));

+ 1 - 0
src/models/dataSourceDetail.js

@@ -12,6 +12,7 @@ export default {
             name: '未命名',
             type: null,
             connectCode: null,
+            connectName: null,
             dbType: null,
             dbName: null,
             address: null,

+ 16 - 0
src/themes/default/chart.less

@@ -1,6 +1,22 @@
 .layout-chart {
     &> .ant-layout-content {
         &> .ant-card {
+            >.ant-card-head {
+                >.ant-card-head-wrapper {
+                    >.ant-card-head-title {
+                        .group {
+                            .ant-tag {
+                                background: transparent;
+                                border: none;
+                                text-decoration: underline;
+                            }
+                            .ant-breadcrumb-separator {
+                                margin: 0;
+                            }
+                        }
+                    }
+                }
+            }
             &> .ant-card-body {
                 background: @content-background-color;
                 .chart-card {

+ 27 - 7
src/themes/default/dashboarddesigner.less

@@ -1,9 +1,14 @@
 .layout-dashboarddesigner {
-    @text-color: #D2E0F3;
+    @text-color: #9EA8B5;
     @text-color-active: #A7B1BE;
     &>.ant-layout-header {
         height: 46px;
         line-height: 42px;
+        >.dashboarddesigner-header .toolbar-title .input-title .ant-input-wrapper {
+            input, .ant-input-group-addon {
+                color: #2C82BE;
+            }
+        }
     }
     &>.ant-layout-content {
         .content {
@@ -18,20 +23,35 @@
                             border-width: 0 1px 0 0;
                             margin: 0;
                             >.form-config {
-                                .ant-divider {
+                                .divider {
                                     color: @text-color;
-                                    &:before, &:after {
-                                        border-color: @text-color;
+                                    margin-top: 12px;
+                                    margin-bottom: 12px;
+                                    &:after {
+                                        content: '';
+                                        border-bottom: 1px solid @text-color;
+                                        position: absolute;
+                                        width: 94%;
+                                        height: 2px;
+                                        /* top: 20px; */
+                                        margin-top: 22px;
+                                        margin-left: -56px;
                                     }
                                 }
                                 .view-types {
                                     .view-type-item {
-                                        border-color: @text-color-active;
-                                        i {
-                                            color: @text-color-active;
+                                        color: @text-color-active;
+                                        &:hover {
+                                            color: #fff;
                                         }
                                     }
                                 }
+                                .cus-filter-button {
+                                    color: @text-color-active;
+                                    &:hover {
+                                        color: #fff;
+                                    }
+                                }
                             }
                         }
                     }

+ 18 - 0
src/themes/default/datasource.less

@@ -1,5 +1,23 @@
 .layout-datasource {
     &> .ant-layout-content {
+        &> .ant-card {
+            >.ant-card-head {
+                >.ant-card-head-wrapper {
+                    >.ant-card-head-title {
+                        .group {
+                            .ant-tag {
+                                background: transparent;
+                                border: none;
+                                text-decoration: underline;
+                            }
+                            .ant-breadcrumb-separator {
+                                margin: 0;
+                            }
+                        }
+                    }
+                }
+            }
+        }
         &> .datasource-body {
             &> .ant-card-body {
                 background: @content-background-color;

+ 24 - 18
src/themes/default/datasourcedetail.less

@@ -15,15 +15,38 @@
     &> .ant-layout-content {
         padding: 12px;
         background: @content-background-color;
+        > .content-datasourcecreate {
+            >.content {
+                >.datasource-steps-container {
+                    .steps-content {
+                        .dataconnect-card {
+                            .ant-card {
+                                .ant-card-head {
+                                    .ant-card-head-wrapper {
+                                        background: #D6EEFE;
+                                        .label {
+                                            font-size: 14px;
+                                            color: #2C82BE;
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
         &> .content-datasourcedetail {
             &> .content {
-                box-shadow: 0 0 10px 0 rgba(41,54,72,0.10);
                 &> .ant-tabs {
                     &> .ant-tabs-bar {
 
                     }
                     &> .ant-tabs-content {
+                        padding: 12px;
                         height: calc(~"100% - 12px");
+                        box-shadow: 0 0 10px 0 rgba(41,54,72,0.10);
+                        background: #fff;
                         &> .tab-datasource {
                             background: @content-background-color;
                             .table-columnconfig {
@@ -34,23 +57,6 @@
                         }
                     }
                 }
-                >.datasource-steps-container {
-                    .steps-content {
-                        .dataconnect-card {
-                            .ant-card {
-                                .ant-card-head {
-                                    .ant-card-head-wrapper {
-                                        background: #D6EEFE;
-                                        .label {
-                                            font-size: 14px;
-                                            color: #2C82BE;
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
             }
         }
     }

TEMPAT SAMPAH
static/images/uas.png