Ant Design Pro 学习之table使用

在table的行render时常用的操作

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

 {

​      title: '更新时间',

​      dataIndex: 'updateTime',

​      sorter: true,

​      render: val => <span>{moment(val).utc().format('YYYY-MM-DD HH:mm:ss')}</span>,

​    },

2、列表上显示图片

    {
      title: '肺功能报告',
      dataIndex: 'url',
      render: text => <img alt="商品图片" style={{ width: 100, height: 50 }} src={text} />,
    },
image

这个图片不能在当前页放大,或者直接在浏览器访问。
如果需要点击后在当前页面放大,这样的效果


放大

可以这么干,先撸一个imageView组件放在src/pages/Common下

import React, { PureComponent } from 'react';
import { Spin, Modal } from 'antd';

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

class ImageView extends PureComponent {
  state = {
    loading: true,
    visible: false,
    imgWidth: 0,
    imgHeight: 0,
  };

  componentDidMount() {
    this.init();
  }

  componentWillReceiveProps(nextProps) {
    const { url } = this.props;
    if (url !== nextProps.url) {
      this.init();
    }
  }

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

  getImageSize = (originImgWidth, originImgHeight) => {
    const { width, height } = 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 { width: imgWidth, height: imgHeight };
  };

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

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

export default ImageView;

然后table上这么用

    {
      title: '图片',
      dataIndex: 'itemImage',
      key: 'itemImage',
      render: (text, record) => <ImageView alt={record.title} url={text} />,
    },

3、长文本使用省略号

Ellipsis
    {
      title: '接收详情',
      dataIndex: 'receiveDesc',
      render: value => (
        <Ellipsis length={5} tooltip>
          {value}
        </Ellipsis>
      ),
    },

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

4、可折叠table

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

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

5、table显示总条数

total
  state: {
    query: { ...defaultQuery },
    list: {
      data: [],
      pagination: {},
    },
    exporting: false,
  },

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

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

<PageTable
            columns={this.columns}
              loading={loading}
              dataSource={list.data}
              pagination={list.pagination}
              onChange={this.handleTableChange}
            />

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

6、表头支持筛选

筛选
{
       title: '人员状态',
       dataIndex: 'jobStatus',
       render:(text)=>{
           const filters = options.jobStatus.filter(s => s.value === text);
           return filters.length ? filters[0].text : '';
        },
       filters: options.jobStatus,
}

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

    componentDidMount() {
        this.props.dispatch({type: 'rwUserOption/init'}).then(() => {
            this.props.dispatch({type: 'rwUserList/reload'});
        });
    }

请求字典数据:

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

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

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

dict.many是这样定义的:

import req from './api-base';

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

export default dict;

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

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

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

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

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

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

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

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

然后写一个DictValue的组件:

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

const dictCache = {};

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

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

export async function getValue(index, value) {
  const indexValues = await getIndexValues(index);
  return memoizeGetValue(indexValues, value);
}

async function getValueName(index, value) {
  const indexValue = await getValue(index, value);
  return indexValue ? indexValue.name : value;
}

export class DictValue extends PureComponent {
  state = {
    loading: false,
    text: '',
  };

  componentDidMount() {
    const { index, value } = this.props;
    this.loadDate(index, value);
  }

  componentWillReceiveProps(nextProps) {
    const { index, value } = nextProps;
    const { props } = this;
    if (props.index !== index || props.value !== value) {
      this.loadDate(index, value);
    }
  }

  loadDate = (index, value) => {
    if (!value) {
      this.setState({ text: '', loading: false });
      return;
    }
    this.setState({ loading: true });
    getValueName(index, value).then(text => {
      this.setState({ text, loading: false });
    });
  };

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

const { Option } = Select;

export class DictSelect extends PureComponent {
  state = {
    data: [],
  };

  componentDidMount() {
    const { index } = this.props;
    getIndexValues(index).then(data => {
      this.setState({ data });
    });
  }

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

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

{
      title: '接收结果',
      dataIndex: 'receiveResult',
      render: value => <DictValue index="sms_receive_result" value={value} />,
},

DictSelect的用法:

<FormItem label="接收结果">
    <DictSelect
       allowClear
       index="sms_receive_result"
       value={query.receiveResult}
       onChange={receiveResult => this.handleSearch({ receiveResult })}
    />
</FormItem>

8、等待。。。

官方API

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,569评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,499评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,271评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,087评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,474评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,670评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,911评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,636评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,397评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,607评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,093评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,418评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,074评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,092评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,865评论 0 196
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,726评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,627评论 2 270

推荐阅读更多精彩内容

  • ORA-00001: 违反唯一约束条件 (.) 错误说明:当在唯一索引所对应的列上键入重复值时,会触发此异常。 O...
    我想起个好名字阅读 4,867评论 0 9
  • ¥开启¥ 【iAPP实现进入界面执行逐一显】 〖2017-08-25 15:22:14〗 《//首先开一个线程,因...
    小菜c阅读 6,209评论 0 17
  • MYSQL 基础知识 1 MySQL数据库概要 2 简单MySQL环境 3 数据的存储和获取 4 MySQL基本操...
    Kingtester阅读 7,649评论 5 116
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,036评论 1 32
  • 一. Java基础部分.................................................
    wy_sure阅读 3,731评论 0 11