Quellcode durchsuchen

列表界面滚动条位置记录逻辑/数据源已修改退出提示

zhuth vor 6 Jahren
Ursprung
Commit
e22c67ab72

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

@@ -35,20 +35,24 @@ class ChartList extends React.Component {
             visibleGroupManageMentBox: false, // 显示分组管理组件
             visibleDataPreviewBox: false,
             noGroup: false, // 显示未分组数据
+            ListScrollTop: 0, // 记录滚动条高度
         }
         this.bodyRef = React.createRef();
     }
 
     componentDidMount() {
-        const { dispatch } = this.props;
+        const { dispatch, chart } = this.props;
         this.setBodyWidth();
         dispatch({ type: 'chart/fetchList' });
         dispatch({ type: 'chart/remoteGroupList' });
+        this.bodyRef.current.parentNode.scrollTo(0, chart.listScrollTop)
         window.addEventListener('resize', this.setBodyWidth);
     }
 
     componentWillUnmount() {
-        window.removeEventListener('resize', this.setBodyWidth)
+        const { dispatch } = this.props;
+        window.removeEventListener('resize', this.setBodyWidth);
+        dispatch({ type: 'chart/setField', name: 'listScrollTop', value: this.bodyRef.current.parentNode.scrollTop });
     }
 
     /**

+ 0 - 36
src/components/common/filterBox/filterOperators.js

@@ -17,12 +17,6 @@ export default {
     }, {
         value: "notEquals",
         label: "不等于"
-    // }, {
-    //     value: "null",
-    //     label: "为空"
-    // }, {
-    //     value: "notNull",
-    //     label: "不为空"
     }],
     string: [{
         value: "include",
@@ -42,12 +36,6 @@ export default {
     }, {
         value: "notEquals",
         label: "不等于"
-    // }, {
-    //     value: "null",
-    //     label: "为空"
-    // }, {
-    //     value: "notNull",
-    //     label: "不为空"
     }],
     scale: [{
         value: ">",
@@ -70,12 +58,6 @@ export default {
     }, {
         value: "between",
         label: "介于"
-    // }, {
-    //     value: "null",
-    //     label: "为空"
-    // }, {
-    //     value: "notNull",
-    //     label: "不为空"
     }],
     time: [{
         value: "=",
@@ -89,12 +71,6 @@ export default {
     }, {
         value: "between",
         label: "介于"
-    // }, {
-    //     value: "null",
-    //     label: "为空"
-    // }, {
-    //     value: "notNull",
-    //     label: "不为空"
     }],
     categorical: [{
         value: "=",
@@ -114,12 +90,6 @@ export default {
     }, {
         value: "notInclude",
         label: "不包含"
-    // }, {
-    //     value: "null",
-    //     label: "为空"
-    // }, {
-    //     value: "notNull",
-    //     label: "不为空"
     }],
     ordinal: [{
         value: ">",
@@ -142,12 +112,6 @@ export default {
     }, {
         value: "between",
         label: "介于"
-    // }, {
-    //     value: "null",
-    //     label: "为空"
-    // }, {
-    //     value: "notNull",
-    //     label: "不为空"
     }],
     "undefined": [{
         value: "null",

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

@@ -1,4 +1,5 @@
 .layout-dashboard {
+    flex-direction: row;
     .ant-layout-sider {
         border-width: 1px;
         border-style: solid;

+ 25 - 4
src/components/dashboard/list.jsx

@@ -32,8 +32,32 @@ class DashboardList extends React.Component {
     }
 
     componentDidMount() {
-        const { dispatch } = this.props;
+        const { dashboard, dispatch } = this.props;
+        this.setScrollTableHeight();
         dispatch({ type: 'dashboard/fetchList', mandatory: true });
+        document.getElementsByClassName('ant-table-body')[0].scrollTo(0, dashboard.listScrollTop);
+    }
+
+    componentWillUnmount() {
+        const { dispatch } = this.props;
+        window.removeEventListener('resize', this.setBodyWidth);
+        dispatch({ type: 'dashboard/setField', name: 'listScrollTop', value: document.getElementsByClassName('ant-table-body')[0].scrollTop });
+    }
+
+    /**
+     * 根据视图设置表格高度以呈现滚动条
+     */
+    setScrollTableHeight() {
+        const view = document.getElementsByClassName('dashboard-view')[0];
+        const mainContentBody = view.getElementsByClassName('dashboard-body')[0];
+        const tableContentBody = mainContentBody.getElementsByClassName('ant-card-body')[0];
+        const tableHeader = tableContentBody.getElementsByClassName('ant-table-header')[0];
+        const tableBody = tableContentBody.getElementsByClassName('ant-table-body')[0];
+
+        // 如果上下padding不一致就有问题了
+        const padding = tableContentBody.children[0].getBoundingClientRect().top - tableContentBody.getBoundingClientRect().top;
+        // table容器高度 - 上下padding - 表头高度 - 边框线宽
+        tableBody.style.maxHeight = `${tableContentBody.offsetHeight - padding * 2 - tableHeader.offsetHeight - 2}px`;
     }
 
     getShareList = () => {
@@ -334,9 +358,6 @@ class DashboardList extends React.Component {
                             </Col>
                         </Row>
                     }>
-                        {/* <div className='dashboard-body'>
-                            { this.generateCard() }
-                        </div> */}
                         <Table
                             className='dashboard-table'
                             columns={dashboardColumns}

+ 17 - 27
src/components/dataSource/list.jsx

@@ -31,21 +31,31 @@ class DataSource extends React.Component {
         this.setState({ visibleTransferBox: false})
     }
     componentDidMount() {
-        const { dispatch } = this.props;
+        const { dispatch, dataSource } = this.props;
         this.setScrollTableHeight();
         dispatch({ type: 'dataSource/fetchList' });
         dispatch({ type: 'dataSource/remoteGroupList' });
+        document.getElementsByClassName('ant-table-body')[0].scrollTo(0, dataSource.listScrollTop);
+    }
+    componentWillUnmount() {
+        const { dispatch } = this.props;
+        dispatch({ type: 'dataSource/setField', name: 'listScrollTop', value: document.getElementsByClassName('ant-table-body')[0].scrollTop });
     }
 
     /**
      * 根据视图设置表格高度以呈现滚动条
      */
     setScrollTableHeight() {
-        const mainContent = document.getElementsByClassName('main-content')[0];
-        const toolbar = mainContent.getElementsByClassName('datasource-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`;
+
+        const mainContentBody = document.getElementsByClassName('datasource-body')[0];
+        const tableContentBody = mainContentBody.getElementsByClassName('ant-card-body')[0];
+        const tableHeader = tableContentBody.getElementsByClassName('ant-table-header')[0];
+        const tableBody = tableContentBody.getElementsByClassName('ant-table-body')[0];
+
+        // 如果上下padding不一致就有问题了
+        const padding = tableContentBody.children[0].getBoundingClientRect().top - tableContentBody.getBoundingClientRect().top;
+        // table容器高度 - 上下padding - 表头高度 - 边框线宽
+        tableBody.style.maxHeight = `${tableContentBody.offsetHeight - padding * 2 - tableHeader.offsetHeight - 2}px`;
     }
 
     onGroup() {
@@ -290,27 +300,6 @@ class DataSource extends React.Component {
             dataIndex: 'dbConfig.name',
             key: 'dbConfig.name',
             width: 100
-        }, {
-            title: '说明',
-            dataIndex: 'description',
-            key: 'description',
-            width: 200,
-            render: (text, record) => {
-                return (
-                    <span>
-                        { (filterItem.name === 'description' && 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>
-                )
-            }
         }, {
             title: '创建人',
             dataIndex: 'creatorName',
@@ -397,6 +386,7 @@ class DataSource extends React.Component {
                         </Row>
                     }>
                         <Table
+                            ref={(node) => { this.tableRef = node}}
                             className='datasource-table'
                             columns={dataSourceColumns}
                             dataSource={

+ 1 - 0
src/components/dataSource/list.less

@@ -48,6 +48,7 @@
         }
     }
     .ant-card-body {
+        height: 100%;
         padding: 0;
         .datasource-table{
             .ant-table {

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

@@ -95,6 +95,7 @@ class DataConnectBox extends React.Component {
             >
                 <Form size='small'>
                     <FormItem
+                        className='required'
                         label='链接名'
                         {...formItemLayout}
                         validateStatus={(dataConnect.newOne.name === undefined || dataConnect.newOne.name) ? 'success' : 'error'}
@@ -108,7 +109,7 @@ class DataConnectBox extends React.Component {
                         >
                         </Input>
                     </FormItem>
-                    <FormItem label='数据库类型' {...formItemLayout}
+                    <FormItem className='required' label='数据库类型' {...formItemLayout}
                         validateStatus={(dataConnect.newOne.dbType === undefined || dataConnect.newOne.dbType) ? 'success' : 'error'}
                         help={(dataConnect.newOne.dbType === undefined || dataConnect.newOne.dbType) ? '' : '数据库类型不能为空'}
                     >
@@ -137,7 +138,7 @@ class DataConnectBox extends React.Component {
                     </FormItem>
                     <Row>
                         <Col span={19}>
-                            <FormItem label='数据库地址' {...{
+                            <FormItem className='required' label='数据库地址' {...{
                                 labelCol: { span: 5 },
                                 wrapperCol: { span: 19 }
                             }}
@@ -155,7 +156,7 @@ class DataConnectBox extends React.Component {
                             </FormItem>
                         </Col>
                         <Col span={5}>
-                            <FormItem className='input-port' label='端口' {...{
+                            <FormItem className='input-port required' label='端口' {...{
                                 labelCol: { span: 12 },
                                 wrapperCol: { span: 12 }
                             }}
@@ -173,7 +174,7 @@ class DataConnectBox extends React.Component {
                             </FormItem>
                         </Col>
                     </Row>
-                    <FormItem label='数据库名(SID)' {...formItemLayout}
+                    <FormItem className='required' label='数据库名' {...formItemLayout}
                         validateStatus={(dataConnect.newOne.dbName === undefined || dataConnect.newOne.dbName) ? 'success' : 'error'}
                         help={(dataConnect.newOne.dbName === undefined || dataConnect.newOne.dbName) ? '' : '数据库名不能为空'}
                     >
@@ -188,7 +189,7 @@ class DataConnectBox extends React.Component {
                     </FormItem>
                     <Row>
                         <Col span={12}>
-                            <FormItem label='用户名' {...{
+                            <FormItem className='required' label='用户名' {...{
                                 labelCol: { span: 8 },
                                 wrapperCol: { span: 16 }
                             }}
@@ -205,7 +206,7 @@ class DataConnectBox extends React.Component {
                             </FormItem>
                         </Col>
                         <Col span={8}>
-                            <FormItem label='密码' {...{
+                            <FormItem className='required' label='密码' {...{
                                 labelCol: { span: 8 },
                                 wrapperCol: { span: 16 }
                             }}

+ 9 - 1
src/components/dataSourceDetail/dataConnectBox.less

@@ -3,7 +3,15 @@
     .ant-modal-body {
         max-height: 60vh;
         overflow-y: auto;
-
+        .ant-form-item.required {
+            .ant-form-item-label label:before{
+                content: '*';
+                position: absolute;
+                font-weight: bold;
+                color: red;
+                left: -8px;
+            }
+        }
         .ant-input-number-handler-wrap {
             display: none;
         }

+ 56 - 6
src/components/dataSourceDetail/header.jsx

@@ -1,10 +1,21 @@
 import React from 'react'
-import { Button } from 'antd'
+import { Button, Popconfirm, Icon } from 'antd'
 import { connect } from 'dva'
 import './header.less'
 
 class DataSourceDetailHeader extends React.Component {
 
+    constructor(props) {
+        super(props);
+        this.state = {
+            visibleConfirm: false,
+        }
+    }
+
+    handleVisibleChange = (visible) => {
+        this.setState({ visibleConfirm: visible });
+    }
+
     render() {
         const { dataSourceDetail, dispatch } = this.props;
         const { code, type, name, columns, targetDirty} = dataSourceDetail;
@@ -12,14 +23,53 @@ class DataSourceDetailHeader extends React.Component {
         return (
             <div className='dataSourcedetail-header'>
                 <div>
-                    <span className='title-label'>{name}</span>
+                    <span className='title-label'>{name || '未命名'}</span>
                 </div>
                 <div className='header-item buttons'>
-                    <Button onClick={(e) => {
-                        dispatch({ type: 'main/redirect', path: '/workshop/datasource' });
+
+                    {code && code !== 'create' && <Popconfirm
+                        overlayClassName={`close-popconfirm${( ( type === 'database' && (!name || targetDirty) ) || ( type === 'file' && +1 === 2) ) ||
+                            (!columns || columns.length === 0) ? ' confirm-disabled' : ''}`}
+                        placement="bottomLeft"
+                        title="离开前保存修改吗?"
+                        visible={this.state.visibleConfirm}
+                        onVisibleChange={this.handleVisibleChange}
+                        onConfirm={() => {
+                            if( ( ( type === 'database' && (!name || targetDirty) ) || ( type === 'file' && +1 === 2) ) ||
+                                (!columns || columns.length === 0)
+                            ) {
+                                // do nothing...
+                            }else {
+                                dispatch({ type: 'dataSource/remoteModify' });
+                                dispatch({ type: 'main/goBack', path: '/workshop/datasource' });
+                            }
+                            this.setState({
+                                visibleConfirm: false
+                            });
+                        }}
+                        onCancel={() => {
+                            this.setState({
+                                visibleConfirm: false
+                            }, () => {
+                                dispatch({ type: 'main/goBack', path: '/workshop/datasource' });
+                            });
+                        }}
+                        okText="保存"
+                        cancelText="不保存"
+                    >
+                        <Button onClick={(e) => {
+                            if(!dataSourceDetail.dirty) {
+                                dispatch({ type: 'main/goBack', path: '/workshop/datasource' });
+                            }
+                        }}>
+                            <Icon type='left' />返回
+                        </Button>
+                    </Popconfirm>}
+                    {!code || code === 'create' && <Button onClick={(e) => {
+                        dispatch({ type: 'main/goBack', path: '/workshop/datasource' });
                     }}>
-                        返回
-                    </Button>
+                        <Icon type='left' />返回
+                    </Button>}
                     {code && code !== 'create' && <Button disabled={ ( ( type === 'database' && (!name || targetDirty) ) || ( type === 'file' && +1 === 2) ) ||
                         (!columns || columns.length === 0)
                     } onClick={() => {

+ 11 - 0
src/components/dataSourceDetail/header.less

@@ -4,4 +4,15 @@
     .title-label {
         font-size: 16px;
     }
+}
+.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;
+    }
+}
 }

+ 1 - 0
src/components/workshop/index.less

@@ -1,4 +1,5 @@
 .layout-workshop {
+    flex-direction: row;
     .sider-workshop {
         padding: 12px;
         border: 1px solid #ccc;

+ 15 - 2
src/models/chart.js

@@ -61,9 +61,22 @@ export default {
                 } }
             ],
             filterItem: { name: 'name', label: '图表名称', type: 'string' }, // 已选过滤字段
+            listScrollTop: 0, // 记录列表界面滚动条位置
         },
     },
     reducers: {
+        setField(state, action) {
+            const { name, value } = action;
+            let obj = {};
+            obj[name] = value;
+            return Object.assign({}, state, obj);
+        },
+        setFields(state, action) {
+            const { fields } = action;
+            let obj = {};
+            fields.map(f => (obj[f.name] = f.value));
+            return Object.assign({}, state, obj);
+        },
         add(state, action) {
             const { data } = action;
             let list = state.list;
@@ -280,9 +293,9 @@ export default {
                             value: data[key]
                         })
                     }
-                    yield put({ type: 'chartDesigner/defaultChangeFields', fields: fields });
+                    yield put({ type: 'chartDesigner/silentChangeFields', fields: fields });
                     
-                    yield put({ type: 'chartDesigner/changeDataSource', dataSource: data.baseConfig.dataSource });
+                    yield put({ type: 'chartDesigner/silentChangeDataSource', dataSource: data.baseConfig.dataSource });
                 }else {
                     message.error('解析图表错误: ' + (res.err || res.data.msg));
                 }

+ 28 - 26
src/models/chartDesigner.js

@@ -70,29 +70,27 @@ export default {
     reducers: {
         /**
          * 更新model字段值
-         * 1. 进入撤销重做历史
+         * 1. 改变dirty
          */
         setField(state, action) {
             const { name, value } = action;
             let obj = {};
             obj[name] = value;
-            let newState = Object.assign({}, state, obj);
-            return Object.assign({}, newState, {dirty: true});
+            return Object.assign({}, state, obj, { dirty: true });
         },
         /**
          * 批量更新model字段值
-         * 1. 进入撤销重做历史
+         * 1. 改变dirty
          */
         setFields(state, action) {
             const { fields } = action;
             let obj = {};
             fields.map(f => (obj[f.name] = f.value));
-            let newState = Object.assign({}, state, obj);
-            return Object.assign({}, newState, {dirty: true});
+            return Object.assign({}, state, obj, { dirty: true });
         },
         /**
          * 更新model字段值
-         * 1. 不进入撤销重做历史
+         * 1. 不改变dirty
          */
         silentSetField(state, action) {
             const { name, value } = action;
@@ -103,7 +101,7 @@ export default {
         },
         /**
          * 批量更新model字段值
-         * 1. 不进入撤销重做历史
+         * 1. 不改变dirty
          */
         silentSetFields(state, action) {
             const { fields } = action;
@@ -118,40 +116,28 @@ export default {
         },
         setDirty(state, action) {
             const { dirty } = action;
-            let newState = Object.assign({}, state, { dirty });
-            return newState;
+            return Object.assign({}, state, { dirty });
         }
     },
     effects: {
-        /**
-         * 初始化批量更新model字段值
-         * 触发数据刷新、不进入撤销重做历史
-         */
-        *defaultChangeFields(action, { select, call, put }) {
-            const { fields } = action;
-            yield put({ type: 'silentSetFields', fields });
+        *changeField(action, { select, call, put }) {
+            const { name, value } = action;
+            yield put({ type: 'setField', name, value });
 
             const { autoRefresh } = action;
             if(autoRefresh === undefined ? true : autoRefresh) {
                 yield put({ type: 'fetchChartData' });
             }
         },
-        /**
-         * 更新model字段值
-         * 可能影响到数据刷新的model字段改变一般用该action
-         */
-        *changeField(action, { select, call, put }) {
+        *silentChangeField(action, { select, call, put }) {
             const { name, value } = action;
-            yield put({ type: 'setField', name, value });
+            yield put({ type: 'silentSetField', name, value });
 
             const { autoRefresh } = action;
             if(autoRefresh === undefined ? true : autoRefresh) {
                 yield put({ type: 'fetchChartData' });
             }
         },
-        /**
-         * 批量更新model字段值
-         */
         *changeFields(action, { select, call, put }) {
             const { fields } = action;
             yield put({ type: 'setFields', fields });
@@ -161,6 +147,15 @@ export default {
                 yield put({ type: 'fetchChartData' });
             }
         },
+        *silentChangeFields(action, { select, call, put }) {
+            const { fields } = action;
+            yield put({ type: 'silentSetFields', fields });
+
+            const { autoRefresh } = action;
+            if(autoRefresh === undefined ? true : autoRefresh) {
+                yield put({ type: 'fetchChartData' });
+            }
+        },
         *changeDataSource(action, { select, call, put }) {
             const { dataSource } = action;
             const chartDesigner = yield select(state => state.present.chartDesigner);
@@ -168,6 +163,13 @@ export default {
             yield put({ type: 'changeField', name: 'baseConfig', value: { ...baseConfig, dataSource } });
             yield put({ type: 'remoteDataColumn', code: dataSource.code });
         },
+        *silentChangeDataSource(action, { select, call, put }) {
+            const { dataSource } = action;
+            const chartDesigner = yield select(state => state.present.chartDesigner);
+            const { baseConfig } = chartDesigner;
+            yield put({ type: 'silentChangeFields', name: 'baseConfig', value: { ...baseConfig, dataSource } });
+            yield put({ type: 'remoteDataColumn', code: dataSource.code });
+        },
         *remoteQucikAdd(action, { select, call, put }) {
             try{
                 const { dataSource } = action;

+ 1 - 0
src/models/dashboard.js

@@ -29,6 +29,7 @@ export default {
                 { name: 'createTime', label: '创建时间', type: 'date' },
             ],
             filterItem: { name: 'name', label: '报表名称', type: 'string' }, // 已选过滤字段
+            listScrollTop: 0, // 记录滚动条位置
         }
     },
     reducers: {

+ 14 - 2
src/models/dataSource.js

@@ -14,7 +14,6 @@ export default {
             currentGroup: null,
             filterItems: [ // 可选过滤字段
                 { name: 'name', label: '名称', type: 'string' },
-                { name: 'description', label: '说明', type: 'string' },
                 { name: 'creatorName', label: '创建人', type: 'string' },
                 { name: 'createTime', label: '创建时间', type: 'date' },
                 { name: 'dbConfig.code', label: '数据链接', type: 'enum', options: () => {
@@ -40,9 +39,22 @@ export default {
                 } },
             ],
             filterItem: { name: 'name', label: '名称', type: 'string' }, // 已选过滤字段
+            listScrollTop: 0, // 记录列表界面滚动条位置
         }
     },
     reducers: {
+        setField(state, action) {
+            const { name, value } = action;
+            let obj = {};
+            obj[name] = value;
+            return Object.assign({}, state, obj);
+        },
+        setFields(state, action) {
+            const { fields } = action;
+            let obj = {};
+            fields.map(f => (obj[f.name] = f.value));
+            return Object.assign({}, state, obj);
+        },
         list(state, action) {
             const { list } = action;
             return { ...state, list };
@@ -303,7 +315,7 @@ export default {
                             value: data[key]
                         })
                     }
-                    yield put({ type: 'dataSourceDetail/setFields', fields: fields });
+                    yield put({ type: 'dataSourceDetail/silentSetFields', fields: fields });
                 }else {
                     message.error('数据源解析错误: ' + (res.err || res.data.msg));
                 }

+ 1 - 0
src/models/dataSourceDetail.js

@@ -7,6 +7,7 @@ export default {
     namespace: 'dataSourceDetail',
     state: {
         originData: {
+            dirty: false,
             code: null,
             name: '未命名',
             type: null,

+ 17 - 8
src/themes/default/base.less

@@ -21,16 +21,25 @@
 }
 
 // Button
-.ant-btn {
-    background: #F5FBFE;
-    border: 1px solid #408DC6;
-}
-.ant-btn-disabled, .ant-btn.disabled, .ant-btn[disabled], .ant-btn-disabled:hover, .ant-btn.disabled:hover, .ant-btn[disabled]:hover, .ant-btn-disabled:focus, .ant-btn.disabled:focus, .ant-btn[disabled]:focus, .ant-btn-disabled:active, .ant-btn.disabled:active, .ant-btn[disabled]:active, .ant-btn-disabled.active, .ant-btn.disabled.active, .ant-btn[disabled].active {
-    background-color: #f5f5f5;
-    border-color: #ccc;
-}
+// .ant-btn {
+//     background: #F5FBFE;
+//     border: 1px solid #408DC6;
+// }
+// .ant-btn-primary {
+//     background-color: #1890ff;
+// }
+// .ant-btn-disabled, .ant-btn.disabled, .ant-btn[disabled], .ant-btn-disabled:hover, .ant-btn.disabled:hover, .ant-btn[disabled]:hover, .ant-btn-disabled:focus, .ant-btn.disabled:focus, .ant-btn[disabled]:focus, .ant-btn-disabled:active, .ant-btn.disabled:active, .ant-btn[disabled]:active, .ant-btn-disabled.active, .ant-btn.disabled.active, .ant-btn[disabled].active {
+//     background-color: #f5f5f5;
+//     border-color: #ccc;
+// }
 
 // Table
+.ant-table-header {
+    margin-bottom: 0 !important;
+}
+.ant-table-body {
+    margin-top: 0 !important;
+}
 .ant-table-small > .ant-table-content > .ant-table-body {
     margin: 0;
 }