Ant Design Pro 学习之table使用


阿里粑粑开源的管理框架Ant Design Pro使用记录之列表的灵活使用。

简书地址

在table的行render时常用的操作

1、时间格式化,指定时区

 {

​      title: '更新时间',

​      dataIndex: 'updateTime',

​      sorter: true,

​      render: val => <span>&#123;moment(val).utc().format('YYYY-MM-DD HH:mm:ss')&#125;</span>,&#125;,

2、列表上显示图片

    &#123;
      title: '肺功能报告',
      dataIndex: 'url',
      render: text => <img alt="商品图片" style=&#123;&#123; width: 100, height: 50 &#125;&#125; src=&#123;text&#125; />,
    &#125;,

表头时间格式化
这个图片不能在当前页放大,或者直接在浏览器访问。
如果需要点击后在当前页面放大,这样的效果
放大
可以这么干,先撸一个imageView组件放在src/pages/Common下

import React, &#123; PureComponent &#125; from 'react';
import &#123; Spin, Modal &#125; from 'antd';

const defaultStyle = &#123;
  borderRadius: 5,
  borderWidth: 1,
  borderStyle: 'solid',
  borderColor: '#d9d9d9',
  width: 70,
  height: 70,
  padding: 5,
  cursor: 'pointer',
  display: 'inline-flex',
  justifyContent: 'center',
  alignItems: 'center',
&#125;;

class ImageView extends PureComponent &#123;
  state = &#123;
    loading: true,
    visible: false,
    imgWidth: 0,
    imgHeight: 0,
  &#125;;

  componentDidMount() &#123;
    this.init();
  &#125;

  componentWillReceiveProps(nextProps) &#123;
    const &#123; url &#125; = this.props;
    if (url !== nextProps.url) &#123;
      this.init();
    &#125;
  &#125;

  init = () => &#123;
    const &#123; url &#125; = this.props;
    if (url) &#123;
      this.setState(&#123; loading: true &#125;);
      const image = new Image();
      image.src = url;
      if (image.complete) &#123;
        const size = this.getImageSize(image.width, image.height);
        this.setState(&#123; loading: false, imgWidth: size.width, imgHeight: size.height &#125;);
      &#125; else &#123;
        image.onload = () => &#123;
          const size = this.getImageSize(image.width, image.height);
          this.setState(&#123; loading: false, imgWidth: size.width, imgHeight: size.height &#125;);
        &#125;;
      &#125;
    &#125;
  &#125;;

  getImageSize = (originImgWidth, originImgHeight) => &#123;
    const &#123; width, height &#125; = this.props;
    const divWidth = (width || defaultStyle.width) - 2 * defaultStyle.padding;
    const divHeight = (height || defaultStyle.height) - 2 * defaultStyle.padding;
    const ratio = this.getRatio(originImgWidth, originImgHeight);
    const imgWidth = originImgWidth > originImgHeight ? divWidth : divWidth * ratio;
    const imgHeight = originImgHeight > originImgWidth ? divHeight : divHeight * ratio;
    return &#123; width: imgWidth, height: imgHeight &#125;;
  &#125;;

  getRatio = (width, height) => (width < height ? width / height : height / width);

  render() &#123;
    const &#123; url, style &#125; = this.props;
    const &#123; loading, visible, imgWidth, imgHeight &#125; = this.state;
    return (
      <span>
        &#123;url ? (
          <span style=&#123;&#123; ...defaultStyle, ...style &#125;&#125;>
            &#123;loading ? (
              <Spin />
            ) : (
              <img
                style=&#123;&#123; width: imgWidth, height: imgHeight &#125;&#125;
                alt="点击预览图片"
                src=&#123;url&#125;
                onClick=&#123;() => &#123;
                  this.setState(&#123; visible: true &#125;);
                &#125;&#125;
              />
            )&#125;
            <Modal
              style=&#123;&#123; top: 20 &#125;&#125;
              visible=&#123;visible&#125;
              footer=&#123;null&#125;
              onCancel=&#123;() => &#123;
                this.setState(&#123; visible: false &#125;);
              &#125;&#125;
            >
              <img alt="" style=&#123;&#123; width: '100%' &#125;&#125; src=&#123;url&#125; />
            </Modal>
          </span>
        ) : (
          '无'
        )&#125;
      </span>
    );
  &#125;
&#125;

export default ImageView;

然后table上这么用

    &#123;
      title: '图片',
      dataIndex: 'itemImage',
      key: 'itemImage',
      render: (text, record) => <ImageView alt=&#123;record.title&#125; url=&#123;text&#125; />,
    &#125;,

3、长文本使用省略号

Ellipsis

    &#123;
      title: '接收详情',
      dataIndex: 'receiveDesc',
      render: value => (
        <Ellipsis length=&#123;5&#125; tooltip>
          &#123;value&#125;
        </Ellipsis>
      ),
    &#125;,

用到的Ellipsis是官网在components提供的通用组件。

4、可折叠table

可折叠table

  handleTableExpand = record => &#123;
    const toTags = items => items.map((value, index) => <Tag key=&#123;index&#125;>&#123;value&#125;</Tag>);
    return (
      <div>
        <DescriptionList size="small" col=&#123;1&#125;>
          <Description term="资源IDS">&#123;toTags(getResources(record.resourceIds))&#125;</Description>
        </DescriptionList>
        <DescriptionList style=&#123;&#123; marginTop: 15 &#125;&#125; size="small">
          <Description term="短信验证码长度">&#123;record.smsCodeLength&#125;</Description>
          <Description term="短信验证码有效期">
            &#123;record.smsCodeLength ? `$&#123;record.smsCodeExpire&#125;分钟` : ''&#125;
          </Description>
          <Description term="短信验证码签名">&#123;record.smsCodeSign&#125;</Description>
        </DescriptionList>
      </div>
    );
  &#125;;

然后在table上指定expandedRowRender={this.handleTableExpand}即可。

5、table显示总条数

total

  state: &#123;
    query: &#123; ...defaultQuery &#125;,
    list: &#123;
      data: [],
      pagination: &#123;&#125;,
    &#125;,
    exporting: false,
  &#125;,

  effects: &#123;
    *fetch(&#123; payload &#125;, &#123; call, put, select &#125;) &#123;
      yield put(&#123; type: 'query', payload &#125;);
      const query = yield select(state => state.smsLog.query);
      const &#123; data &#125; = yield call(page, query);
      yield put(&#123;
        type: 'list',
        payload: &#123;
          data: data.content,
          pagination: &#123;
            current: data.number + 1,
            pageSize: data.size,
            total: Number(data.totalElements),
          &#125;,
        &#125;,
      &#125;);
    &#125;,
&#125;

渲染时指定pagination的包括了total字段,所以就会显示出总条数。

<PageTable
            columns=&#123;this.columns&#125;
              loading=&#123;loading&#125;
              dataSource=&#123;list.data&#125;
              pagination=&#123;list.pagination&#125;
              onChange=&#123;this.handleTableChange&#125;
            />

list就是从接口获取的数据,

6、表头支持筛选

筛选

&#123;
       title: '人员状态',
       dataIndex: 'jobStatus',
       render:(text)=>&#123;
           const filters = options.jobStatus.filter(s => s.value === text);
           return filters.length ? filters[0].text : '';
        &#125;,
       filters: options.jobStatus,
&#125;

页面加载时加载个数据字典的值

    componentDidMount() &#123;
        this.props.dispatch(&#123;type: 'rwUserOption/init'&#125;).then(() => &#123;
            this.props.dispatch(&#123;type: 'rwUserList/reload'&#125;);
        &#125;);
    &#125;

请求字典数据:

import dict from '../../services/api-dict'

const optionKeys = [
    "RWGH-USER-XIAOWEI",
    "RWGH-USER-STATUS",
    'RWGH-TRAIN-ATTENDANCE'
];

export default &#123;
    namespace: 'rwUserOption',
    state: &#123;
        sex:[
            &#123;text: '男', value: '0'&#125;,
            &#123;text: '女', value: '1'&#125;
        ],
        //小微
        xiaowei: [],
        //工作状态
        jobStatus: [],
        attendances: []
    &#125;,
    effects: &#123;
        * init(&#123;payload&#125;, &#123;put, call&#125;) &#123;
            const [xiaowei, jobStatus, attendances] = yield call(dict.many, optionKeys);
            const mapper = (items) => &#123;
                return items.map(item => &#123;
                    const &#123;dictValueName, dictValueCode&#125; = item;
                    return &#123;text: dictValueName, value: dictValueCode&#125;;
                &#125;)
            &#125;;
            yield put(&#123;type: 'changeXiaoWei', payload: mapper(xiaowei)&#125;);
            yield put(&#123;type: 'changeJobStatus', payload: mapper(jobStatus)&#125;);
            yield put(&#123;type: 'changeAttendances', payload: mapper(attendances)&#125;);
        &#125;
    &#125;,
    reducers: &#123;
        changeXiaoWei(state, &#123;payload: xiaowei&#125;) &#123;
            return &#123;...state, xiaowei&#125;
        &#125;,
        changeJobStatus(state, &#123;payload: jobStatus&#125;) &#123;
            return &#123;...state, jobStatus&#125;
        &#125;,
        changeAttendances(state, &#123;payload: attendances&#125;)&#123;
            return &#123;...state, attendances&#125;
        &#125;
    &#125;
&#125;

dict.many是这样定义的:

import req from './api-base';

const dict = &#123;
    one: (indexCode, cache = true) => &#123;
        return new Promise(resolve => &#123;
            const dict = sessionStorage.getItem(indexCode);
            if (cache && dict) &#123;
                resolve(dict);
            &#125; else &#123;
                req.get("/questionnaire/admin/sys/dict/values", &#123;indexCode&#125;).then(data => &#123;
                    sessionStorage.setItem(dict, data.data);
                    resolve(data.data)
                &#125;)
            &#125;
        &#125;);
    &#125;,
    many: (indexCodes, cache = true) => &#123;
        return Promise.all(indexCodes.map(code => dict.one(code, cache)));
    &#125;
&#125;

export default dict;

其中人员状态的数据结构如下:

&#123;
    "code": 0,
    "message": "操作成功",
    "data": [
        &#123;
            "dictValueId": 162,
            "dictIndexId": 148,
            "dictValueCode": "0",
            "dictValueName": "在职",
            "deleteFlag": "0",
            "dictValueSort": 0
        &#125;,
        &#123;
            "dictValueId": 163,
            "dictIndexId": 148,
            "dictValueCode": "1",
            "dictValueName": "产假",
            "deleteFlag": "0",
            "dictValueSort": 1
        &#125;,
        &#123;
            "dictValueId": 164,
            "dictIndexId": 148,
            "dictValueCode": "2",
            "dictValueName": "离职",
            "deleteFlag": "0",
            "dictValueSort": 2
        &#125;,
        &#123;
            "dictValueId": 165,
            "dictIndexId": 148,
            "dictValueCode": "3",
            "dictValueName": "退休",
            "deleteFlag": "0",
            "dictValueSort": 3
        &#125;,
        &#123;
            "dictValueId": 167,
            "dictIndexId": 148,
            "dictValueCode": "4",
            "dictValueName": "转岗",
            "deleteFlag": "0",
            "dictValueSort": 4
        &#125;,
        &#123;
            "dictValueId": 168,
            "dictIndexId": 148,
            "dictValueCode": "5",
            "dictValueName": "病假",
            "deleteFlag": "0",
            "dictValueSort": 5
        &#125;
    ]
&#125;

这个筛选是多选的,选中后点击确定会触发查询,参数会xiaoweis=XSJDXW%2CXHYXW像这样拼接到url里。

7、结果转enum显示文本,如订单状态等。

const sendResultDom = &#123;
  success: <Badge status="success" text="成功" />,
  error: <Badge status="error" text="失败" />,
&#125;;
    &#123;
      title: '发送结果',
      dataIndex: 'sendResult',
      render: test => (test === '0' ? sendResultDom.success : sendResultDom.error),
    &#125;,

这种适合于这个对应值比较稳定变动较小的情况,如果是经常修改或者适配新增,又不改代码的话,就需要以数据字典的形式提供给页面字典数据,然后用组件去适配显示内容。

比如数据字典的sysDict/value/code/list?indexCode=sms_receive_result数据结构如下:

&#123;
    "code": 0,
    "message": "操作成功",
    "data": [
        &#123;
            "id": "88685502718279680",
            "isDelete": false,
            "createTime": 1553756821000,
            "updateTime": 1553756821000,
            "parentId": "88685399781670912",
            "name": "成功",
            "code": "0",
            "sort": 0,
            "type": 1,
            "remark": ""
        &#125;,
        &#123;
            "id": "88685542446727168",
            "isDelete": false,
            "createTime": 1553756830000,
            "updateTime": 1553756830000,
            "parentId": "88685399781670912",
            "name": "失败",
            "code": "1",
            "sort": 1,
            "type": 1,
            "remark": ""
        &#125;,
        &#123;
            "id": "88685595827634176",
            "isDelete": false,
            "createTime": 1553756843000,
            "updateTime": 1553756843000,
            "parentId": "88685399781670912",
            "name": "等待",
            "code": "-1",
            "sort": 2,
            "type": 1,
            "remark": ""
        &#125;
    ]
&#125;

然后写一个DictValue的组件:

/* eslint-disable */
import React, &#123; PureComponent &#125; from 'react';
import &#123; Spin, Select &#125; from 'antd';
import _ from 'lodash';
import memoizeOne from 'memoize-one';
import &#123; valueListByIndexCode &#125; from '@/services/dict';

const dictCache = &#123;&#125;;

const filterValue = (dicts, value) => _.find(dicts, d => d.code === value);
const memoizeGetValue = memoizeOne(filterValue);

async function getIndexValues(index) &#123;
  const values = dictCache[index];
  if (values) return values;
  const &#123; data &#125; = await valueListByIndexCode(&#123; indexCode: index &#125;);
  const newValues = data.map(d => (&#123; name: d.name, code: d.code, remark: d.remark &#125;));
  dictCache[index] = newValues;
  return newValues;
&#125;

export async function getValue(index, value) &#123;
  const indexValues = await getIndexValues(index);
  return memoizeGetValue(indexValues, value);
&#125;

async function getValueName(index, value) &#123;
  const indexValue = await getValue(index, value);
  return indexValue ? indexValue.name : value;
&#125;

export class DictValue extends PureComponent &#123;
  state = &#123;
    loading: false,
    text: '',
  &#125;;

  componentDidMount() &#123;
    const &#123; index, value &#125; = this.props;
    this.loadDate(index, value);
  &#125;

  componentWillReceiveProps(nextProps) &#123;
    const &#123; index, value &#125; = nextProps;
    const &#123; props &#125; = this;
    if (props.index !== index || props.value !== value) &#123;
      this.loadDate(index, value);
    &#125;
  &#125;

  loadDate = (index, value) => &#123;
    if (!value) &#123;
      this.setState(&#123; text: '', loading: false &#125;);
      return;
    &#125;
    this.setState(&#123; loading: true &#125;);
    getValueName(index, value).then(text => &#123;
      this.setState(&#123; text, loading: false &#125;);
    &#125;);
  &#125;;

  render() &#123;
    const &#123; loading, text &#125; = this.state;
    return <span>&#123;loading ? <Spin size="small" /> : `$&#123;text&#125;`&#125;</span>;
  &#125;
&#125;

const &#123; Option &#125; = Select;

export class DictSelect extends PureComponent &#123;
  state = &#123;
    data: [],
  &#125;;

  componentDidMount() &#123;
    const &#123; index &#125; = this.props;
    getIndexValues(index).then(data => &#123;
      this.setState(&#123; data &#125;);
    &#125;);
  &#125;

  render() &#123;
    const &#123; props &#125; = this;
    const &#123; data &#125; = this.state;
    return (
      <Select placeholder="请选择" &#123;...props&#125;>
        &#123;data.map(item => (
          <Option key=&#123;item.code&#125; value=&#123;item.code&#125;>
            &#123;item.name&#125;
          </Option>
        ))&#125;
      </Select>
    );
  &#125;
&#125;

其中valueListByIndexCode就是获取到上面结构数据的api,不用特殊说明了。
这个类包含了两个方法,一个是DictValue,用来从key获取value,一个是DictSelect提供一下该字典值得下拉选项。
DictValue的用法:

&#123;
      title: '接收结果',
      dataIndex: 'receiveResult',
      render: value => <DictValue index="sms_receive_result" value=&#123;value&#125; />,
&#125;,

DictSelect的用法:

<FormItem label="接收结果">
    <DictSelect
       allowClear
       index="sms_receive_result"
       value=&#123;query.receiveResult&#125;
       onChange=&#123;receiveResult => this.handleSearch(&#123; receiveResult &#125;)&#125;
    />
</FormItem>

8、等待。。。

官方API


评论
  目录