Browse Source

操作日志/数据源、图表名称长度限制/

zhuth 6 years ago
parent
commit
e0fb93dfe1

+ 65 - 36
src/components/chartDesigner/header.jsx

@@ -1,8 +1,9 @@
 import React from 'react'
-import { Input, Icon, Button, Popconfirm, Tooltip, Modal } from 'antd'
+import { Input, Icon, Button, Popconfirm, Tooltip, Modal, Form } from 'antd'
 import { connect } from 'dva'
 import DeleteBox from '../common/deleteBox/deleteBox'
 import './header.less'
+const FormItem = Form.Item
 
 class Header extends React.Component {
     constructor(props) {
@@ -11,7 +12,10 @@ class Header extends React.Component {
             visibleConfirm: false,
             visibleCopyBox: false,
             visibleDeleteBox: false,
-            newHeaderLabel: ''
+            newHeaderLabel: '',
+            validInfo: {
+                header: { status: 'success', help: '' }
+            },
         }
     }
 
@@ -35,23 +39,33 @@ class Header extends React.Component {
         return currentUser.code === creatorCode;
     }
 
+    isValid = () => {
+        const { chartDesigner, main } = this.props;
+        const { header } = chartDesigner; 
+
+        return !!header.label && header.label.trim().length > 0 && header.label.length <= 50;
+    }
+
     render() {
         const { chartDesigner, dispatch } = this.props;
-        const { visibleCopyBox, newHeaderLabel, visibleDeleteBox } = this.state;
+        const { visibleCopyBox, newHeaderLabel, visibleDeleteBox, validInfo } = this.state;
         return (
             <div className='header'>
                 <div className='header-item toolbar-back'>
                     {this.isOwner() && <Popconfirm
+                        overlayClassName={`close-popconfirm${this.isValid() ? '' : ' confirm-disabled'}`}
                         placement="bottomLeft"
                         title="离开前保存修改吗?"
                         visible={this.state.visibleConfirm}
                         onVisibleChange={this.handleVisibleChange}
                         onConfirm={() => {
+                            if(this.isValid()) {
+                                dispatch({ type: 'chart/remoteModify' });
+                                dispatch({ type: 'main/goBack', path: '/chart' });
+                            }
                             this.setState({
                                 visibleConfirm: false
                             });
-                            dispatch({ type: 'chart/remoteModify' });
-                            dispatch({ type: 'main/goBack', path: '/chart' });
                         }}
                         onCancel={() => {
                             this.setState({
@@ -77,46 +91,61 @@ class Header extends React.Component {
                     </Button>}
                 </div>
                 <div className='header-item toolbar-title'>
-                    <Input
-                        key={chartDesigner.header.label}
-                        className='input-title'
-                        readOnly={!this.isOwner()}
-                        defaultValue={chartDesigner.header.label}
-                        addonAfter={this.isOwner() ? <Icon type="edit" 
-                            onClick={(e) => {
-                                const input = e.currentTarget.parentElement.parentElement.getElementsByTagName('input')[0];
-                                input && input.focus()
+                    <FormItem
+                        validateStatus={validInfo.header.status}
+                        help={validInfo.header.help}
+                    >
+                        <Input
+                            key={chartDesigner.header.label}
+                            className='input-title'
+                            readOnly={!this.isOwner()}
+                            defaultValue={chartDesigner.header.label}
+                            addonAfter={this.isOwner() ? <Icon type="edit" 
+                                onClick={(e) => {
+                                    const input = e.currentTarget.parentElement.parentElement.getElementsByTagName('input')[0];
+                                    input && input.focus()
+                                }}
+                            /> : ''}
+                            onBlur={(e) => {
+                                dispatch({ type: 'chartDesigner/setField', name: 'header', value: { label: e.target.value + '' } });
                             }}
-                        /> : ''}
-                        onBlur={(e) => {
-                            let value = e.target.value;
-                            if(!!value.trim()) {
-                                dispatch({ type: 'chartDesigner/setField', name: 'header', value: { label: e.target.value } });
-                            }else {
-                                e.target.value = chartDesigner.header.label;
-                            }
-                        }}
-                        onPressEnter={(e) => {
-                            let value = e.target.value;
-                            if(!!value.trim()) {
-                                dispatch({ type: 'chartDesigner/setField', name: 'header', value: { label: e.target.value } });
-                            }else {
-                                e.target.value = chartDesigner.header.label;
-                            }
-                        }}
-                    />
+                            onPressEnter={(e) => {
+                                dispatch({ type: 'chartDesigner/setField', name: 'header', value: { label: e.target.value + '' } });
+                            }}
+                            onChange={e => {
+                                let val = e.target.value + '';
+                                let status, help;
+                                if(val.trim().length === 0) {
+                                    status = 'error';
+                                    help = '数据源名称不能为空';
+                                }else if(val.trim().length > 50) {
+                                    status = 'error';
+                                    help = '数据源名称不能超过50个字符';
+                                }else {
+                                    status = 'success';
+                                    help = '';
+                                }
+                                window.clearTimeout(this.headerTimeout);
+                                this.headerTimeout = window.setTimeout(() => {
+                                    this.setState({
+                                        validInfo: { ...validInfo, header: { status, help } }
+                                    });
+                                }, 100);
+                            }}
+                        />
+                    </FormItem>
                 </div>
                 <div className='header-item toolbar-buttons'>
-                    {this.isOwner() && <Button onClick={() => {
+                    {this.isOwner() && <Button disabled={!this.isValid()} onClick={() => {
                         if(chartDesigner.code && chartDesigner.code !== -1) {
                             dispatch({ type: 'chart/remoteModify' });
                         }else {
                             dispatch({ type: 'chart/remoteAdd' });
                         }
                         dispatch({ type: 'chartDesigner/setDirty', dirty: false });
-                    }}><Icon type='save' />保存</Button>}
+                    }}>保存</Button>}
                     {this.isOwner() && <Tooltip title='复制新增'>
-                        <Button className='tools-btn' icon='copy' onClick={() => {
+                        <Button className='tools-btn' onClick={() => {
                             this.setState({
                                 visibleCopyBox: true,
                                 newHeaderLabel: chartDesigner.header.label + '_副本'
@@ -143,7 +172,7 @@ class Header extends React.Component {
                         this.setState({
                             visibleDeleteBox: true
                         });
-                    }}><Icon type='delete' />删除</Button>}
+                    }}>删除</Button>}
                     {visibleDeleteBox && <DeleteBox
                         visibleBox={visibleDeleteBox}
                         text={<div><span>确定要删除图表【{chartDesigner.header.label}】吗?</span></div>}

+ 4 - 0
src/components/chartDesigner/header.less

@@ -34,6 +34,10 @@
                 color: #40a9ff;
             }
         }
+        .ant-form-explain {
+            margin-right: -580px;
+            margin-top: -30px;
+        }
     }
     .toolbar-buttons {
         button {

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

@@ -131,7 +131,6 @@ class DataSource extends React.Component {
     getParentsByCode = (groupCode) => {
         const groupData = this.props.dataSource.groupList;
         let group = groupData.find(g => g.code === groupCode);
-        console.log(group);
         if(group) {
             return this.getParents(group);
         }else {
@@ -507,7 +506,6 @@ class DataSource extends React.Component {
             </Layout>
         )
 
-
     }
 }
 

+ 60 - 11
src/components/dataSourceDetail/baseConfig.jsx

@@ -7,6 +7,16 @@ const SelectOption = Select.Option
 
 class DataSourceBaseConfig extends React.Component {
 
+    constructor(props) {
+        super(props);
+        this.state = {
+            validInfo: {
+                name: { status: 'success', help: '' },
+                description: { status: 'success', help: '' },
+            }
+        }
+    }
+
     generateOptions () {
         const { dataConnect } = this.props;
         const { list } = dataConnect;
@@ -48,6 +58,7 @@ class DataSourceBaseConfig extends React.Component {
 
     render() {
         const { dataConnect, dataSource, dataSourceDetail, dispatch } = this.props;
+        const { validInfo } = this.state;
         const treeData = arrayToTree(dataSource.groupList, '-1', 'code', 'pcode', 'children');
         const formItemLayout = {
             labelCol: { span: 4 },
@@ -73,15 +84,35 @@ class DataSourceBaseConfig extends React.Component {
         return (
             <Form className='form-base' size='small'>
                 <Divider orientation="left">基本配置</Divider>
-                <FormItem label='数据源名称' {...formItemLayout}>
+                <FormItem label='数据源名称' {...formItemLayout}
+                    validateStatus={validInfo.name.status}
+                    help={validInfo.name.help}
+                >
                     <Input
-                        value={dataSourceDetail.name}
+                        key={dataSourceDetail.name}
+                        defaultValue={dataSourceDetail.name}
                         onBlur={(e) => {
-                            // dispatch({ type: 'dataSourceDetail/setField', name: 'name', value: e.target.value });
-                            // dispatch({ type: 'dataSource/remoteModify' });
+                            dispatch({ type: 'dataSourceDetail/setField', name: 'name', value: e.target.value+'' });
                         }}
-                        onChange={(e) => {
-                            dispatch({ type: 'dataSourceDetail/setField', name: 'name', value: e.target.value });
+                        onChange={e => {
+                            let val = e.target.value + '';
+                            let status, help;
+                            if(val.trim().length === 0) {
+                                status = 'error';
+                                help = '数据源名称不能为空';
+                            }else if(val.trim().length > 50) {
+                                status = 'error';
+                                help = '数据源名称不能超过50个字符';
+                            }else {
+                                status = 'success';
+                                help = '';
+                            }
+                            window.clearTimeout(this.nameTimeout);
+                            this.nameTimeout = window.setTimeout(() => {
+                                this.setState({
+                                    validInfo: { ...validInfo, name: { status, help } }
+                                });
+                            }, 100);
                         }}>
                     </Input>
                 </FormItem>
@@ -227,15 +258,33 @@ class DataSourceBaseConfig extends React.Component {
                     >
                     </Cascader>
                 </FormItem>
-                <FormItem className='textarea-desc' label='说明' {...formItemLayout}>
+                <FormItem className='textarea-desc' label='说明' {...formItemLayout}
+                     validateStatus={validInfo.description.status}
+                     help={validInfo.description.help}
+                >
                     <Input.TextArea
+                        key={dataSourceDetail.description}
                         autosize={{ minRows: 2, maxRows: 5 }}
-                        value={dataSourceDetail.description}
-                        onChange={(e) => {
+                        defaultValue={dataSourceDetail.description}
+                        onBlur={(e) => {
                             dispatch({ type: 'dataSourceDetail/setField', name: 'description', value: e.target.value });
                         }}
-                        onBlur={(e) => {
-                            // dispatch({ type: 'dataSource/remoteModify' });
+                        onChange={e => {
+                            let val = e.target.value + '';
+                            let status, help;
+                            if(val.length > 150) {
+                                status = 'error';
+                                help = '说明不能超过150个字符';
+                            }else {
+                                status = 'success';
+                                help = '';
+                            }
+                            window.clearTimeout(this.descriptionTimeout);
+                            this.descriptionTimeout = window.setTimeout(() => {
+                                this.setState({
+                                    validInfo: { ...validInfo, description: { status, help } }
+                                });
+                            }, 100);
                         }}
                     />
                 </FormItem>

+ 3 - 2
src/components/dataSourceDetail/columnConfig.jsx

@@ -250,9 +250,10 @@ class DataSourceColumnConfig extends React.Component {
             render: (text, record) => {
                 return(
                     <Input
-                        value={text}
+                        key={text}
+                        defaultValue={text}
                         placeholder={record.description ? record.description.substring(0, 10) : record.name}
-                        onChange={(e) => {
+                        onBlur={(e) => {
                             const value = e.target.value;
                             let columns = dataSourceDetail.columns.map(c => {
                                 if(c.key === record.key) {

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

@@ -57,7 +57,7 @@ class DataConnectBox extends React.Component {
         }
 
         return flag && !!newOne && !!newOne.name && newOne.name.length <= 50 && !!newOne.dbType && !!newOne.address && !!newOne.port &&
-            !!newOne.dbName && !!newOne.userName && newOne.description.length <= 500 && (newOne.boxOperation === 'create' ? !!newOne.password : true);
+            !!newOne.dbName && !!newOne.userName && newOne.description.length <= 150 && (newOne.boxOperation === 'create' ? !!newOne.password : true);
     }
 
     render() {
@@ -289,7 +289,7 @@ class DataConnectBox extends React.Component {
                                 window.clearTimeout(this.nameTimeout);
                                 this.nameTimeout = window.setTimeout(() => {
                                     this.setState({
-                                        validInfo: { ...validInfo, description: { status: (!!val && (val+'').trim().length > 500) ? 'error' : 'success', help: (!!val && (val+'').trim().length > 500) ? '说明不能超过500个字符' : '' } }
+                                        validInfo: { ...validInfo, description: { status: (!!val && (val+'').trim().length > 150) ? 'error' : 'success', help: (!!val && (val+'').trim().length > 150) ? '说明不能超过150个字符' : '' } }
                                     });
                                 }, 100);
                             }}

+ 16 - 13
src/components/dataSourceDetail/header.jsx

@@ -21,26 +21,31 @@ class DataSourceDetailHeader extends React.Component {
         this.setState({ visibleSaveConfirm: visible });
     }
 
+    isValid = () => {
+        const { dataSourceDetail, dispatch } = this.props;
+        const { code, type, name, columns, targetDirty, description } = dataSourceDetail; 
+        return type === 'database' ? (
+            !!name && name.length < 50 && !targetDirty && !!columns && columns.length > 0 && (description === undefined || description === null || description.length <= 150)
+        ) : ( type === 'file' ? (
+            +1 === 2
+        ) : false )
+    }
+
     render() {
         const { dataSourceDetail, dispatch } = this.props;
-        const { code, type, name, columns, targetDirty} = dataSourceDetail;
+        const { code, type, name, columns, targetDirty, description } = dataSourceDetail;
 
         return (
             <div className='dataSourcedetail-header'>
                 <div>
                     {code && code !== 'create' && <Popconfirm
-                        overlayClassName={`close-popconfirm${( ( type === 'database' && (!name || targetDirty) ) || ( type === 'file' && +1 === 2) ) ||
-                            (!columns || columns.length === 0) ? ' confirm-disabled' : ''}`}
+                        overlayClassName={`close-popconfirm${this.isValid() ? '' : ' 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 {
+                            if(this.isValid()) {
                                 dispatch({ type: 'dataSource/remoteModify' });
                                 dispatch({ type: 'main/redirect', path: '/workshop/datasource' });
                             }
@@ -67,8 +72,8 @@ class DataSourceDetailHeader extends React.Component {
                         </Button>
                     </Popconfirm>}
                 </div>
-                <div>
-                    <span className='title-label'>{name || '未命名'}</span>
+                <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) => {
@@ -96,9 +101,7 @@ class DataSourceDetailHeader extends React.Component {
                         okText="确定"
                         cancelText="取消"
                     >
-                        <Button disabled={ ( ( type === 'database' && (!name || targetDirty) ) || ( type === 'file' && +1 === 2) ) ||
-                            (!columns || columns.length === 0)
-                        } onClick={() => {
+                        <Button disabled={!this.isValid()} onClick={() => {
                             this.setState({
                                 visibleSaveConfirm: true
                             });

+ 120 - 50
src/components/logs/logs.jsx

@@ -3,6 +3,7 @@ import { Layout, Row, Col, Input, Table, Card } from 'antd'
 import { connect } from 'dva'
 import { dateFormat } from '../../utils/baseUtils'
 import './logs.less'
+import ListFilter from '../common/listFilter/index';
 const { Content } = Layout
 const { Search } = Input
 
@@ -10,13 +11,16 @@ class Logs extends React.Component {
     constructor(props) {
         super(props);
         this.state = {
-            pageSize: 10,
+            total: 0,
+            pageSize: 0,
             tableBodyHeight: 0,
         }
     };
 
     componentDidMount() {
+        const { dispatch } = this.props;
         this.tableSize();
+        dispatch({ type: 'logs/fetchList' });
         window.addEventListener('resize', this.tableSize);
     }
 
@@ -25,81 +29,147 @@ class Logs extends React.Component {
     }
 
     tableSize = () => {
-        const tableEl = document.getElementsByClassName('logs-table')[0];
-        const tableScrollEl = tableEl.getElementsByClassName('ant-table-scroll')[0];
+        const cardBody = document.getElementsByClassName('ant-card-body')[0];
+        const table = cardBody.getElementsByClassName('logs-table')[0];
+        const tableHeader = table.getElementsByClassName('ant-table-thead')[0];
+        const padding = table.getBoundingClientRect().top - cardBody.getBoundingClientRect().top;
+        const tableHeaderHeight = tableHeader.offsetHeight;
+        let tableBodyHeight = cardBody.offsetHeight - padding * 2 - tableHeaderHeight - 40;
+        let pageSize = Math.ceil((tableBodyHeight) / 38);
         this.setState({
-            tableBodyHeight: tableScrollEl.offsetHeight - 38,
-            pageSize: Math.ceil((tableScrollEl.offsetHeight - 38) / 38)
+            pageSize,
+            tableBodyHeight
         });
     }
 
+    onSort = (list) => {
+        return list.sort((a, b) => new Date(b.date) - new Date(a.date));
+    }
+
+    onSearch = () => {
+        const { logs } = this.props;
+        const { list, filterLabel, filterItem } = logs;
+        const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
+        let _filterLabel = filterLabel ? (filterLabel + '').replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') : ''; // 添加转义符号
+        let filterReg = new RegExp('(' + _filterLabel + '){1}', 'ig');
+
+        let dataList = list.map(l => {
+            if(filterItem.type === 'date') {
+                if(filterLabel===""){
+                    return { ...l };
+                }else if(filterLabel.indexOf('#')>-1){
+                    let start = filterLabel.split('#')[0]
+                    let end = filterLabel.split('#')[1]
+                    let nowTime = new Date(l[filterItem.name]).getTime();
+                    if(nowTime>=start && nowTime<=end){
+                        return { ...l };
+                    }
+                    return null;
+                }else{
+                    return null;
+                }
+            }else {
+                return ((l[filterItem.name] + '').search(filterReg) > -1) ? { ...l } : null
+            }
+        }).filter(l => l !== null);
+
+        return dataList;
+    }
+
     render() {
-        const { logs } = this.props 
+        const { logs } = this.props;
+        const { pageSize, tableBodyHeight } = this.state;
+        const { filterItem, filterLabel } = logs;
+
+        let dataList = this.onSort(this.onSearch());
+        let total = dataList.length;
+
+        const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
+
         const logsColumns = [{
-            title: '界面名称',
-            dataIndex: 'name',
-            key: 'name',
-            width: 200
-        }, {
-            title: '界面模块',
+            title: '模块',
             dataIndex: 'module',
             key: 'module',
             width: 100,
+            render: text => {
+                return (filterItem.name === 'module' && 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
+            }
+        }, {
+            title: '名称',
+            dataIndex: 'name',
+            key: 'name',
+            width: 200,
+            render: text => {
+                return (filterItem.name === 'name' && 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
+            }
         }, {
             title: '操作人',
             dataIndex: 'operator',
             key: 'operator',
-            width: 100
+            width: 100,
+            render: text => {
+                return (filterItem.name === 'operator' && 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
+            }
         }, {
             title: '操作时间',
-            dataIndex: 'createTime',
-            key: 'createTime',
-            render: (text, record) => dateFormat(text, 'yyyy-MM-dd hh:mm:ss'),
+            dataIndex: 'date',
+            key: 'date',
             width: 100
-        }, {
-            title: '操作',
-            dataIndex: 'action',
-            key: 'action',
-            width: 150
         }];
 
         return (      
-            <Layout className='logs-view'>
+            <Layout className='layout-logs'>
                 <Content>
-                    <Card className='logs-body' title={
-                            <Row className='logs-tools' type='flex' justify='space-between'>
-                                <Col className='search'>
-                                    <Col>
-                                        <Search
-                                            //value={dataSource.filterLabel}
-                                            placeholder="请输入关键字"
-                                            // onChange={e => {
-                                            //     //dispatch({ type: 'dataSource/setFilterLabel', label: e.target.value });
-                                            // }}
-                                        />
-                                    </Col>
-                                </Col>
-                            </Row>
-                        }>
+                    <Card bordered={false} className='logs-body' title={
+                        <Row className='logs-tools' type='flex' justify='space-between'>
+                            <Col className='search'>
+                                <ListFilter modelName='logs' model={logs} />
+                            </Col>
+                        </Row>
+                    }>
                         <Table
                             className='logs-table'
                             columns={logsColumns}
-                            dataSource={
-                                logs.dataList
-                                // this.onSort(
-                                //     this.onSearch(this.onGroup(), dataSource.filterLabel)
-                                // )
-                            }
+                            dataSource={dataList}
                             size='small'
-                            scroll={{
-                                x: false, 
-                                y: 440,
-                            }}
+                            // loading={loading}
                             pagination={{
-                                total: 20
+                                pageSize,
+                                total,
+                                // simple: true,
+                                showTotal: (total, range) => {
+                                    return `第${range[0]}到第${range[1]}条数据,共${total}条数据`;
+                                },
+                                // onChange: (page, pageSize) => {
+                                //     typeof fetchFunction === 'function' && fetchFunction(page, pageSize);
+                                // }
                             }}
-                        />,
-                        mountNode
+                            scroll={{ x: false, y: tableBodyHeight }}
+                        />
                     </Card>
                 </Content>
             </Layout>

+ 34 - 8
src/components/logs/logs.less

@@ -1,13 +1,39 @@
-.logs-tabs {
+.layout-logs {
     height: 100%;
-    .ant-tabs-bar {
-        margin: 0;
-    }
-    .ant-tabs-content {
-        height: 100%;
-        border: none !important;
-        .ant-tabs-tabpane {
+    padding: 12px;
+    display: flex;
+    flex-direction: column;
+    background: #fff;
+    box-shadow: 0 0 10px 0 rgba(41, 54, 72, 0.1);
+
+    >.ant-layout-content {
+        >.logs-body {
             height: 100%;
+            display: flex;
+            flex-direction: column;
+            >.ant-card-head {
+                min-height: 42px;
+                height: 42px;
+                padding: 0;
+                >.ant-card-head-wrapper {
+                    >.ant-card-head-title {
+                        padding: 0;
+                    }
+                }
+            }
+            >.ant-card-body {
+                flex: auto;
+                overflow: hidden;
+                background: #FAFAFA;
+                padding: 12px;
+                border: 1px solid rgba(0, 0, 0, 0.1);
+                .ant-table-body {
+                    margin-top: 0;
+                }
+                .ant-pagination {
+                    margin: 9px 0;
+                }
+            }
         }
     }
 }

+ 1 - 1
src/components/setting/index.jsx

@@ -34,7 +34,7 @@ class Setting extends React.Component {
             >
                 <Link to='/setting/admin'><div className={`link-btn${(paths[1] === 'admin' || !paths[1]) ? ' selected' : ''}`}><Button className='ant-btn-block' type={(paths[1] === 'admin' || !paths[1]) ? 'primary' : 'default'} >用户管理</Button></div></Link>
                 <Link to='/setting/authority'><div className={`link-btn${(paths[1] === 'authority' || !paths[1]) ? ' selected' : ''}`}><Button disabled className='ant-btn-block' type={paths[1] === 'authority' ? 'primary' : 'default'} >权限管理</Button></div></Link>
-                <Link to='/setting/logs'><div className={`link-btn${(paths[1] === 'logs' || !paths[1]) ? ' selected' : ''}`}><Button disabled className='ant-btn-block' type={paths[1] === 'logs' ? 'primary' : 'default'} >操作日志</Button></div></Link>
+                <Link to='/setting/logs'><div className={`link-btn${(paths[1] === 'logs' || !paths[1]) ? ' selected' : ''}`}><Button className='ant-btn-block' type={paths[1] === 'logs' ? 'primary' : 'default'} >操作日志</Button></div></Link>
             </Sider>
             <Content className='content-setting'>
                 <Switch>

+ 91 - 130
src/models/logs.js

@@ -1,141 +1,102 @@
+import { message } from 'antd'
+import * as service from '../services/index'
+import URLS from '../constants/url'
+
 export default {
     namespace: 'logs',
     state: {
-        dataList:[{
-            name:'报表',
-            module:'筛选模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'修改操作'
-        },{
-            name:'报表',
-            module:'全部模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'新增'
-        },{
-            name:'报表',
-            module:'报表设计模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'删除'
-        },{
-            name:'报表',
-            module:'筛选模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'修改操作'
-        },{
-            name:'报表',
-            module:'全部模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'新增'
-        },{
-            name:'报表',
-            module:'报表设计模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'删除'
-        },{
-            name:'报表',
-            module:'筛选模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'修改操作'
-        },{
-            name:'报表',
-            module:'全部模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'新增'
-        },{
-            name:'报表',
-            module:'报表设计模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'删除'
-        },{
-            name:'报表',
-            module:'筛选模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'修改操作'
-        },{
-            name:'报表',
-            module:'全部模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'新增'
-        },{
-            name:'报表',
-            module:'报表设计模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'删除'
-        },{
-            name:'报表',
-            module:'筛选模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'修改操作'
-        },{
-            name:'报表',
-            module:'全部模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'新增'
-        },{
-            name:'报表',
-            module:'报表设计模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'删除'
-        },{
-            name:'报表',
-            module:'筛选模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'修改操作'
-        },{
-            name:'报表',
-            module:'全部模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'新增'
-        },{
-            name:'报表',
-            module:'报表设计模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'删除'
-        },{
-            name:'报表',
-            module:'筛选模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'修改操作'
-        },{
-            name:'报表',
-            module:'全部模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'新增'
-        },{
-            name:'报表',
-            module:'报表设计模块',
-            operator:'张三',
-            createTime:1555316130068,
-            action:'删除'
-        }]
+        originData: {
+            list: [],
+            total: 0,
+            filterLabel: '',
+            filterItems: [
+                { name: 'module', label: '模块', type: 'enum', options: [
+                    { name: '数据链接', label: '数据链接' },
+                    { name: '数据源', label: '数据源' },
+                    { name: '图表', label: '图表' },
+                    { name: '报表', label: '报表' },
+                    { name: '数据源分组', label: '数据源分组' },
+                    { name: '图表分组', label: '图表分组' },
+                ] },
+                { name: 'name', label: '名称', type: 'string' },
+                { name: 'operator', label: '操作人', type: 'string' },
+                { name: 'date', label: '操作时间', type: 'date' },
+            ], // 可选过滤字段
+            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);
+        },
+        setFilterItem(state, action) {
+            const { item } = action;
+            return Object.assign({}, state, {filterItem: item, filterLabel: ''});
+        },
+        setFilterLabel(state, action) {
+            const { label } = action;
+            return Object.assign({}, state, {filterLabel: label});
+        },
+        reset(state, action) {
+            return Object.assign({}, state, state.originData);
+        },
     },
     effects: {
-        
+       *fetchList(action, { select, call, put }) {
+            const { pageNum, pageSize } = action;
+            const body = {
+                pageNum: pageNum || 1,
+                pageSize: pageSize || 999
+            }
+            try{
+                const logs = yield select(state => state.present.logs);
+                if(!action.mandatory && logs.list.length > 0) {
+                    return;
+                }
+                const res = yield call(service.fetch, {
+                    url: URLS.MESSAGELOG_LIST,
+                    method: 'GET',
+                    body
+                });
+                if(!res.err && res.data.code > 0) {
+                    let total = res.data.data.total;
+                    let list = res.data.data.list.map(d => {
+                        return {
+                            code: d.id + '',
+                            date: d.date,
+                            content: d.content,
+                            module: d.module,
+                            name: d.order,
+                            result: d.result,
+                            search: d.search,
+                            operator: d.userName
+                        }
+                    })
+                    yield put({ type: 'setFields', fields: [
+                        { name: 'list', value: list },
+                        { name: 'total', value: total },
+                    ] });
+                }else {
+                    message.error('请求操作日志列表失败: ' + (res.err || res.data.msg));
+                }
+            }catch(e) {
+                message.error('请求操作日志列表失败: ' + e.message);
+            }
+        } 
     },
     subscriptions: {
-
+        setup({ dispatch, history }) {
+            dispatch({ type: 'reset' });
+        }
     }
 };