Antd 实现表格行上下移动

Posted by violetks on July 9, 2020

场景:点击表格操作栏中的移动行按钮,实现表格行的移动。也就是两行数据的交换。第一行提示最顶部无法上移,最后一行提示无法下移。

一、使用 useSelector 获取到列表数据,传给 Antd 表格组件的 dataSource

const { projectList } = useSelector(state => state.project);

// 获取列表数据
const getProjectList = () => {
  dispatch({
    type: 'project/getProjectList',
    payload: { pageNo, pageSize, ...searchInfo }
  });
}

<Table
  rowKey='projectId'
  columns={columns}
  dataSource={projectList}
  pagination={pagination}
/>

二、设置表格的 columns 属性

这里render函数有第三个参数index,可获取到表格行序号。CSS 实现两个按钮上下排列。

const columns = [
  ...
  {
    key: 'action',
    title: '操作',
    width: 150,
    render: (text, record, index) => {
      return (
        <Fragment>
          <span
            className='ht-action-span'
            style={{ position: 'relative', width: '14px', height: '25px', display: 'inline-block' }}>
            <CaretUpOutlined
              style={{
                color: index === 0 ? 'gray' : '',
                position: 'absolute', top: '8px'
              }}
              onClick={() => up(index, record)} />
            <CaretDownOutlined
              style={{
                color: index === projectList.length - 1 ? 'gray' : '',
                position: 'absolute', top: '20px'
              }}
              onClick={() => down(index, record)} />
          </span>
          <span
            className='ht-action-span'
            style={{ margin: "0 8px" }}
            onClick={() => editProject(record)}>
            <EditOutlined />
          </span>
          <span
            className='ht-action-span'
            onClick={() => deleteProject(record.projectId)}>
            <DeleteOutlined />
          </span>
        </Fragment>
      )
    }
  }
];

三、columns 操作列有上下移动两个按钮,给它们绑定点击事件

这里通过reducer改变数据,实现移动。

之前通过setState设置了一个表格的数据源,结果造成首次进入页面表格数据为空,需要再点一下数据才出现,而且编辑等也不能自动刷新页面数据。 打印发现useSelector有延迟的效果,会给你返回多个projectList,但是前几个是空数组。

const up = (index, record) => {
  if (index === 0) {
    return message.warning("最顶部无法上移");
  }
  let list = projectList;
  // let prev = list[index - 1];
  // list.splice(index - 1, 1);
  // list.splice(index, 0, prev);
  // 使用 ES6 优化
  [list[index - 1], list[index]] = [list[index], list[index - 1]];
  dispatch({
    type: 'project/setProjectList',
    payload: {
      projectList: [...list]
    }
  })
}

const down = (index, record) => {
  if (index === projectList.length - 1) {
    return message.warning("最底部无法下移");
  }
  // let next = projectList[index + 1];
  // projectList.splice(index + 1, 1);
  // projectList.splice(index, 0, next);
  let list = projectList;
  [list[index], list[index - 1]] = [list[index - 1], list[index]];
  dispatch({
    type: 'project/setProjectList',
    payload: {
      projectList: [...list]
    }
  })
}

四、dva-loading实践用法

dva-loading用于监听当前异步加载方法的状态,异步加载中状态为true,异步加载完成状态为false

const isLoading = loading.effects['user/query'];

其中user/query是 model 中的异步请求方法。loading在异步请求发出那一刻会持续监听该异步请求方法的状态,在异步请求结束之前isLoading的值一直是true,当此次异步请求结束时isLoading的值变成false,同时loading对象停止监听。

用法一:在 Antd 的Table 组件中使用loading属性判断当前是否有异步加载,显示加载效果。

import React from 'react';
import { Table } from 'antd';
import { useDispatch, useSelector } from 'dva';

const Project = () => {
  const dispatch = useDispatch();
  const { projectList } = useSelector(state => state.project);
  const loading = useSelector(state => state.loading.effects['project/getProjectList']);

  // 获取列表数据
  const getProjectList = () => {
    dispatch({
      type: 'project/getProjectList',
      payload: { pageNo, pageSize, ...searchInfo }
    });
  }

  return (
    <Table
      rowKey='projectId'
      columns={columns}
      dataSource={projectList}
      pagination={pagination}
      loading={loading}
    />
  )
}

export default Project;

用法二:提交按钮如登录等使用loading控制effects提交路径。

const loading = useSelector(state => state.loading.effects);

<Button
  type="primary"
  htmlType="submit"
  loading={loading['auth/login']}
>
  登录
</Button>