react 項目結合 antd 實現強大的後臺管理系統表格組件

react 項目結合 antd 實現強大的後臺管理系統表格組件

簡介: antd 本身的表格組件十分強大,十分靈活,這裏基於 antd 的 Table 組件做業務封裝,支持功能:表格索引,loading,分頁,按鈕操作,數據格式化,數據選擇等。

項目主要依賴:(先安裝,步驟略)

create-react-app:3.0.0

{
  "react": "16.8.6",
  "react-router-dom": "5.0.0",
  "antd": "^3.19.2",
  "axios": "^0.19.0",
  "prop-types": "^15.7.2"
}

1.組件
src/components/AppTable/index.jsx

import React, { Component, Fragment } from 'react';
import { Table, Button, Modal, Layout, Icon } from 'antd';
import PropTypes from 'prop-types';
const { Content } = Layout;

class AppTable extends Component {
  state = {
    // 大圖數據
    imgSrc: ''
  };

  // props類型檢查
  static propTypes = {
    // 操作欄寬度
    operationWidth: PropTypes.number,
    // 配置
    config: PropTypes.object.isRequired,
    // 是否需要操作欄
    hasOperation: PropTypes.bool,
    // 是否需要索引
    hasIndex: PropTypes.bool,
    // 是否需要選擇
    hasSelect: PropTypes.bool,
    // 監聽分頁改變
    handlePageChange: PropTypes.func,
    // 監聽選擇表格
    handleSelecet: PropTypes.func,
    // 監聽操作欄
    handleTableOption: PropTypes.func,
    // 點擊表格單元格
    handleClickCell: PropTypes.func,
    // 上邊距
    marginTop: PropTypes.number
  };

  // 默認的props
  static defaultProps = {
    // 操作欄寬度
    operationWidth: 100,
    // 是否需要操作欄
    hasOperation: true,
    // 是否需要索引
    hasIndex: true,
    // 是否需要選擇
    hasSelect: false,
    // 監聽分頁改變
    handlePageChange: null,
    // 監聽選擇表格
    handleSelecet: null,
    // 監聽操作欄
    handleTableOption: null,
    // 點擊表格單元格
    handleClickCell: null,
    // 上邊距
    marginTop: 20
  };

  componentWillUnmount() {
    this.setState = (state, callback) => {
      return;
    };
  }

  // 點擊查看大圖
  handleClickImage(src) {
    Modal.info({
      title: '大圖',
      okText: '確定',
      style: { top: '15vh' },
      content: (
        <div style={{ textAlign: 'center' }}>
          <img src={src} alt='大圖' />
        </div>
      )
    });
  }

  render() {
    let {
      config,
      operationWidth,
      hasOperation,
      hasIndex,
      hasSelect,
      handlePageChange,
      handleSelecet,
      handleTableOption,
      marginTop
    } = this.props;
    let { columns, pagination } = config;

    // 特殊表格處理(如需更多特殊顯示,添加case即可解決)
    columns.forEach(item => {
      if (item.format) {
        switch (item.format) {
          // 圖片顯示
          case 'image':
            item.render = (value, row, index) => {
              return (
                <img
                  key={row.key}
                  onClick={this.handleClickImage.bind(this, value)}
                  width={30}
                  src={value}
                  alt='圖片損壞'
                />
              );
            };
            break;
          // 金額顯示
          case 'money':
            item.render = value => '¥' + value / 100; // 顯示爲金額,返回的單位是分,處理時除以100換算成元
            break;
          default:
        }
      }
    });

    // 默認添加索引
    if (hasIndex && columns.length) {
      const Index = columns[0];
      const { current, pageSize } = pagination;
      const indexItem = {
        title: '#',
        key: 'index',
        fixed: 'left',
        format: 'index',
        width: 60,
        render: (value, row, index) => (
          <span key={index}>{pageSize * (current - 1) + index + 1}</span>
        )
      };
      if (Index.title !== '#') {
        columns.unshift(indexItem);
      } else {
        columns[0] = indexItem;
      }
    }

    // 默認添加操作欄
    const buttonContent = option => {
      let type = '';
      switch (option) {
        case '查看':
          type = 'eye';
          break;
        case '顯示':
          type = 'eye';
          break;
        case '隱藏':
          type = 'eye-invisible';
          break;
        case '編輯':
          type = 'edit';
          break;
        case '刪除':
          type = 'delete';
          break;
        case '排序':
          type = 'sort-ascending';
          break;
        default:
          break;
      }
      if (type) {
        return (
          <Fragment>
            <Icon type={type} style={{ paddingRight: 5 }} /> {option}
          </Fragment>
        );
      } else {
        return option;
      }
    };

    const operation = columns[columns.length - 1];
    if (columns.length && hasOperation && operation.title !== '操作') {
      columns.push({
        title: '操作',
        dataIndex: 'operation',
        width: operationWidth,
        format: 'operation',
        key: 'operation',
        fixed: 'right',
        render: (value, row, index) => {
          const buttons = row.buttonsName ? row.buttonsName : [];
          return (
            <div>
              {buttons.map((option, index) => {
                return (
                  <Button
                    size='small'
                    type={index === 0 ? 'primary' : ''}
                    key={index}
                    style={{ marginLeft: 10 }}
                    onClick={() => handleTableOption(row, option)}
                  >
                    {buttonContent(option)}
                  </Button>
                );
              })}
            </div>
          );
        }
      });
    }

    // 添加可以是否選擇表格
    const rowSelection = {
      onChange: (key, rows) => {
        handleSelecet(key, rows);
      }
    };
    const selectConfig = hasSelect ? { rowSelection } : {};

    // 默認表格配置
    const defaultConfig = {
      bordered: true,
      scroll: { x: 960 }
    };

    // 默認分頁配置
    const defaultPagination = {
      showQuickJumper: true,
      showSizeChanger: true,
      pageSizeOptions: ['20', '40', '100']
    };
    config.pagination = { ...defaultPagination, ...pagination };

    return (
      <Content
        style={{
          background: '#fff',
          padding: 24,
          marginTop
        }}
      >
        {this.props.children}
        <Table
          {...defaultConfig}
          {...selectConfig}
          {...config}
          onChange={handlePageChange}
        />
      </Content>
    );
  }
}

export default AppTable;

補充全局less(需添加到項目中)

:global(#root .ant-table-thead > tr > th) {
  padding: 8px 7px 7px 22px;
  height: 50px;
  box-sizing: border-box;
  overflow: hidden;
  white-space: nowrap;
}

:global(#root .ant-table-tbody > tr > td) {
  padding: 8px 7px 7px 22px;
  height: 50px;
  box-sizing: border-box;
  overflow: hidden;
  white-space: nowrap;
}

2.使用

import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import AppTable from '@/components/AppTable';

// 後端接口,根據自己項目更換
import UserServe from '@/api/user';

@withRouter // 修飾器需添加babel插件
class User extends Component {
  state = {
    // 用戶列表
    userList: [],
    // 分頁總數
    total: 0,
    // 表格loading
    loading: false,
    // 分頁參數
    params: {
      perPage: 5,
      page: 1
    },
    // 操作欄按鈕
    buttonsName: {
      normal: ['查看', '排序']
      // special: ['排序']
    },
    // 表格操作欄寬度
    operationWidth: 100,
    // 表格配置
    columns: [
      {
        title: '頭像',
        dataIndex: 'avatarUrl',
        width: 80,
        key: 'avatarUrl',
        format: 'image' // 顯示爲圖片
      },
      {
        title: '暱稱',
        dataIndex: 'nickname',
        key: 'nickname',
        width: 140
      },
      {
        title: '真實姓名',
        dataIndex: 'userName',
        key: 'userName',
        width: 140
      },
      {
        title: '電話',
        dataIndex: 'userPhone',
        key: 'userPhone',
        width: 140
      },
      {
        title: '用戶身份',
        dataIndex: 'userType',
        key: 'userType',
        width: 140
      },
      {
        title: '賬戶餘額',
        dataIndex: 'lastAmount',
        key: 'lastAmount',
        width: 140,
        format: 'money' // 顯示爲金額,返回的單位是分,處理時除以100換算成元
      },
      {
        title: '預訂次數',
        dataIndex: 'reserveTimes',
        key: 'reserveTimes',
        width: 140
      },

      {
        title: '上次預訂時間',
        dataIndex: 'lastReserveTime',
        key: 'lastReserveTime'
      }
    ]
  };

  componentDidMount() {
    this.getList();
  }

  componentWillUnmount() {
    this.handleTableOption = null;
    this.handlePageChange = null;
    this.handleSelecet = null;
    this.setState = (state, callback) => {
      return;
    };
  }

  // 獲取用戶列表
  getList = async () => {
    try {
      this.setState({ loading: true });
      const {
        buttonsName: { normal }
      } = this.state;

      // 拿到的服務端數據格式見步驟3
      let {
        items,
        page: { total }
      } = await UserServe.getUserList(this.state.params);
      items.forEach(item => {
        item.buttonsName = normal;
        item.key = item.userUuid;
      });

      this.setState({ userList: items, total, loading: false });
    } catch (error) {
      console.log(error);
    }
  };

  // 操作欄
  handleTableOption = (row, option) => {
    if (option === '查看') {
      const { history } = this.props;
      history.push(`/user/details?userUuid=${row.userUuid}`);
    }
  };

  // 分頁監聽
  handlePageChange = pageInfo => {
    const { current, pageSize } = pageInfo;
    this.setState(
      () => {
        return { params: { perPage: pageSize, page: current } };
      },
      () => {
        this.getList();
      }
    );
  };

  render() {
    const {
      userList: dataSource,
      total,
      loading,
      params,
      columns,
      operationWidth
    } = this.state;
    const tableConfig = {
      columns,
      dataSource,
      pagination: {
        total,
        pageSize: params.perPage,
        current: params.page
      },
      loading
    };

    return (
      <div>
        <AppTable
          config={tableConfig}
          operationWidth={operationWidth}
          handleTableOption={this.handleTableOption}
          handlePageChange={this.handlePageChange}
        />
      </div>
    );
  }
}

export default User;

3.補充表格數據

{
  "data": {
    "items": [
      {
        "avatarUrl": "https://wx.qlogo.cn/mmopen/vi_32/H40abp83zlOAiaqGanY4VpH7kzy6uyOr30Gm634ru0obw9UfSLic4OBxeuH7Oosud15BCwfiazBoCALBIbdsCSjsg/132",
        "lastAmount": 0,
        "lastReserveTime": "2019-08-28 14:38:00",
        "nickname": "\u65e5\u4e45\u751f\u60c5",
        "reserveTimes": 0,
        "userName": null,
        "userPhone": null,
        "userType": "\u666e\u901a\u7528\u6237",
        "userUuid": "60C8546BC95E11E9BD27525400AE34BF"
      },
      {
        "avatarUrl": "https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTIia1wXh7HxQ2C2CkVyjQpH7zOK2hpDeN75yH0C0wS9z8U8eUt8uibK9ckKAekWSPibDScicaMaoPlqHA/132",
        "lastAmount": 0,
        "lastReserveTime": "2019-08-23 11:46:53",
        "nickname": "\u8def\u4eba\u7532",
        "reserveTimes": 0,
        "userName": null,
        "userPhone": null,
        "userType": "\u666e\u901a\u7528\u6237",
        "userUuid": "A561122FC55811E9BD27525400AE34BF"
      },
      {
        "avatarUrl": "https://wx.qlogo.cn/mmopen/vi_32/uJpqs0geoUyTbKHdibj3VL05wUn0IgVXosu4D3WdqdcwjhoJ75ukt6an5nSZRYfQzfPvLkhk9e26Ol4ufrkibqvg/132",
        "lastAmount": 0,
        "lastReserveTime": "2019-08-21 14:44:52",
        "nickname": "\u5c0f\u871c\u8702",
        "reserveTimes": 0,
        "userName": null,
        "userPhone": null,
        "userType": "\u666e\u901a\u7528\u6237",
        "userUuid": "2DC231A1C3DF11E9BD27525400AE34BF"
      },
      {
        "avatarUrl": "https://wx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKD8ffFgoFh3BXZtyP032lhBWJaBUUXzGxgl4IXHf01bLicvPlb4nUpqU2JpcpFRicgW9p0X8S5OcQw/132",
        "lastAmount": 0,
        "lastReserveTime": "2019-07-20 20:58:52",
        "nickname": "\u5e03\u5170\u742a",
        "reserveTimes": 0,
        "userName": null,
        "userPhone": null,
        "userType": "\u666e\u901a\u7528\u6237",
        "userUuid": "1FD14EFBAAEE11E9BD27525400AE34BF"
      },
      {
        "avatarUrl": "https://wx.qlogo.cn/mmopen/vi_32/U10j5gX0UTCI5Sjbh7wakujtcofvHS4FWhHToJSqrFCSe7bUN1YuX1qkaEWbxQ0B09PXlUabeLC7bAN0rYlXkA/132",
        "lastAmount": 0,
        "lastReserveTime": "2019-07-17 14:44:43",
        "nickname": "\u7f57\u4f1fHalo",
        "reserveTimes": 0,
        "userName": null,
        "userPhone": "13684001024",
        "userType": "\u666e\u901a\u7528\u6237",
        "userUuid": "5B67D4BDA85E11E9BD27525400AE34BF"
      }
    ],
    "page": {
      "currentPage": 1,
      "firstPage": 1,
      "hasNext": true,
      "hasPrev": false,
      "lastPage": 5,
      "nextPage": 2,
      "pageCount": 5,
      "pages": [1, 2, 3, 4, 5],
      "perPage": 5,
      "prevPage": 0,
      "total": 24,
      "totalPages": 5
    }
  },
  "status": 1
}

4.使用效果
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章