ant design pro 技巧之实现列表页多标签

像上图那样,我们经常给列表页做分类的,



这种多标签如何处理呢?

肯定是要得到动态数据对吧。

每次点击的时候要发请求,得到不同的数据。

其实 ant design pro 是支持的。

在 index.tsx 中的 protable 里加上这个就好。

javascript 复制代码
toolbar={{
          menu: {
            type: 'tab',
            activeKey: activeKey,
            items: [
              {
                label: <FormattedMessage id="platform.all" defaultMessage="All" />,
                key: '',
              },
              {
                label: <FormattedMessage id="platform.online" defaultMessage="Online" />,
                key: 'true',
              },
              {
                label: <FormattedMessage id="platform.offline" defaultMessage="Offline" />,
                key: 'false',
              },
            ],
            onChange: (key: any) => {
              setActiveKey(key);
              if (actionRef.current) {
                actionRef.current.reload();
              }
            },
          },
        }}

完整代码是这样的:

javascript 复制代码
import { addItem, queryList, removeItem, updateItem } from '@/services/ant-design-pro/api';
import { PlusOutlined } from '@ant-design/icons';
import type { ActionType, ProColumns, ProDescriptionsItemProps } from '@ant-design/pro-components';
import { FooterToolbar, PageContainer, ProTable } from '@ant-design/pro-components';
import { FormattedMessage, useAccess } from '@umijs/max';
import { Button, message, Modal, Switch, Tooltip } from 'antd';
import React, { useRef, useState } from 'react';
import type { FormValueType } from './components/Update';
import Update from './components/Update';
import Create from './components/Create';
import Show from './components/Show';
import { useIntl } from '@umijs/max';
import { carTypeMap } from '@/utils/constants';
import CopyToClipboard from '@/components/CopyToClipboard';
import ConfigureForm from './components/ConfigureForm';

/**
 * @en-US Add node
 * @zh-CN 添加节点
 * @param fields
 */
const handleAdd = async (fields: API.ItemData) => {
  const hide = message.loading(<FormattedMessage id="adding" defaultMessage="Adding..." />);
  try {
    await addItem('/platforms', { ...fields });
    hide();
    message.success(<FormattedMessage id="add.success" defaultMessage="Added successfully" />);
    return true;
  } catch (error: any) {
    hide();
    message.error(
      error?.response?.data?.message ?? (
        <FormattedMessage id="add.failed" defaultMessage="Adding failed, please try again!" />
      ),
    );
    return false;
  }
};

/**
 * @en-US Update node
 * @zh-CN 更新节点
 *
 * @param fields
 */
const handleUpdate = async (fields: FormValueType) => {
  const hide = message.loading(<FormattedMessage id="updating" defaultMessage="Updating..." />);
  try {
    await updateItem(`/platforms/${fields._id}`, fields);
    hide();

    message.success(<FormattedMessage id="update.success" defaultMessage="Updated successfully" />);
    return true;
  } catch (error: any) {
    hide();
    message.error(
      error?.response?.data?.message ?? (
        <FormattedMessage id="update.failed" defaultMessage="Update failed, please try again!" />
      ),
    );
    return false;
  }
};

/**
 *  Delete node
 * @zh-CN 删除节点
 *
 * @param selectedRows
 */
const handleRemove = async (ids: string[]) => {
  const hide = message.loading(<FormattedMessage id="deleting" defaultMessage="Deleting..." />);
  if (!ids) return true;
  try {
    await removeItem('/platforms', {
      ids,
    });
    hide();
    message.success(
      <FormattedMessage
        id="delete.success"
        defaultMessage="Deleted successfully and will refresh soon"
      />,
    );
    return true;
  } catch (error: any) {
    hide();
    message.error(
      error.response.data.message ?? (
        <FormattedMessage id="delete.failed" defaultMessage="Delete failed, please try again" />
      ),
    );
    return false;
  }
};

const TableList: React.FC = () => {
  const intl = useIntl();
  /**
   * @en-US Pop-up window of new window
   * @zh-CN 新建窗口的弹窗
   *  */
  const [createModalOpen, handleModalOpen] = useState<boolean>(false);
  /**2024fc.xyz
   * @en-US The pop-up window of the distribution update window
   * @zh-CN 分布更新窗口的弹窗
   * */
  const [updateModalOpen, handleUpdateModalOpen] = useState<boolean>(false);

  const [showDetail, setShowDetail] = useState<boolean>(false);

  const actionRef = useRef<ActionType>();
  const [currentRow, setCurrentRow] = useState<API.ItemData>();
  const [selectedRowsState, setSelectedRows] = useState<API.ItemData[]>([]);
  const access = useAccess();
  const [activeKey, setActiveKey] = useState<string | undefined>('');
  const [configureModalVisible, setConfigureModalVisible] = useState<boolean>(false);

  /**
   * @en-US International configuration
   * @zh-CN 国际化配置
   * */

  const columns: ProColumns<API.ItemData>[] = [
    {
      title: intl.formatMessage({ id: 'platform.name' }),
      dataIndex: 'name',
      width: 150,
      copyable: true,
      render: (dom, entity) => {
        return (
          <a
            onClick={() => {
              setCurrentRow(entity);
              setShowDetail(true);
            }}
          >
            {dom}
          </a>
        );
      },
    },
    {
      title: intl.formatMessage({ id: 'platform.image' }),
      dataIndex: 'image',
      width: 100,
      hideInSearch: true,
      valueType: 'image',
    },
    {
      title: intl.formatMessage({ id: 'platform.region' }),
      dataIndex: 'region',
      width: 200,
      render: (_, record: any) =>
        record.region && (
          <>
            {record.region.name} <CopyToClipboard text={record.region.name} />
          </>
        ),
    },
    {
      title: intl.formatMessage({ id: 'platform.price' }),
      dataIndex: 'price',
      width: 150,
      hideInSearch: true,
      render: (text, record: any) => (
        <Tooltip title={`€ ${record.priceInEuro}`}>
          <strong>$ {text}</strong>/{carTypeMap[record.carType as keyof typeof carTypeMap]}
        </Tooltip>
      ),
    },
    {
      title: intl.formatMessage({ id: 'platform.tips' }),
      ellipsis: true,
      width: 150,
      hideInTable: true,
      hideInSearch: true,
      dataIndex: 'tips',
    },
    {
      title: intl.formatMessage({ id: 'platform.boardingMethod' }),
      dataIndex: 'boardingMethod',
      width: 150,
      hideInSearch: true,
      hideInTable: true,
      valueEnum: {
        '': { text: intl.formatMessage({ id: 'all' }), status: 'Default' },
        fullCar: { text: intl.formatMessage({ id: 'platform.fullCar' }) },
        batchCar: { text: intl.formatMessage({ id: 'platform.batchCar' }) },
      },
    },
    {
      title: intl.formatMessage({ id: 'platform.deliveryMethod' }),
      dataIndex: 'deliveryMethod',
      width: 150,
      hideInSearch: true,
      hideInTable: true,
      valueEnum: {
        '': { text: intl.formatMessage({ id: 'all' }), status: 'Default' },
        autoDelivery: { text: intl.formatMessage({ id: 'platform.autoDelivery' }) },
        inviteLink: { text: intl.formatMessage({ id: 'platform.inviteLink' }) },
      },
    },
    {
      title: intl.formatMessage({ id: 'platform.status' }),
      dataIndex: 'isOnline',
      width: 150,
      hideInSearch: true,
      render: (_, record: any) => (
        <Switch
          checkedChildren={intl.formatMessage({ id: 'platform.online' })}
          unCheckedChildren={intl.formatMessage({ id: 'platform.offline' })}
          checked={record.isOnline}
          onChange={async () => {
            await handleUpdate({ _id: record._id, isOnline: !record.isOnline });
            if (actionRef.current) {
              actionRef.current.reload();
            }
          }}
        />
      ),
    },
    {
      title: intl.formatMessage({ id: 'platform.remark' }),
      ellipsis: true,
      width: 150,
      hideInSearch: true,
      dataIndex: 'remark',
    },
    {
      title: intl.formatMessage({ id: 'platform.userCount' }),
      hideInSearch: true,
      width: 150,
      hideInTable: true,
      dataIndex: 'userCount',
    },
    {
      title: intl.formatMessage({ id: 'platform.user' }),
      hideInSearch: true,
      width: 150,
      dataIndex: 'user',
      render: (_, record) => record.user && record.user.name,
    },
    {
      title: <FormattedMessage id="creation_time" defaultMessage="Creation Time" />,
      width: 250,
      dataIndex: 'createdAt',
      valueType: 'dateTime',
      hideInSearch: true,
    },
    {
      title: <FormattedMessage id="platform.description" defaultMessage="platform.description" />,
      width: 250,
      dataIndex: 'descriptions',
      hideInSearch: true,
      hideInTable: true,
      render: (text) => (Array.isArray(text) ? text.join(', ') : text),
    },
    {
      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="Operating" />,
      dataIndex: 'option',
      valueType: 'option',
      fixed: 'right',
      render: (_, record) => [
        <a
          key="update"
          onClick={() => {
            handleUpdateModalOpen(true);
            setCurrentRow(record);
          }}
        >
          <FormattedMessage id="edit" defaultMessage="Edit" />
        </a>,
        access.canSuperAdmin && (
          <a
            key="delete"
            onClick={() => {
              return Modal.confirm({
                title: intl.formatMessage({ id: 'confirm.delete' }),
                onOk: async () => {
                  await handleRemove([record._id!]);
                  setSelectedRows([]);
                  actionRef.current?.reloadAndRest?.();
                },
                content: intl.formatMessage({ id: 'confirm.delete.content' }),
                okText: intl.formatMessage({ id: 'confirm' }),
                cancelText: intl.formatMessage({ id: 'cancel' }),
              });
            }}
          >
            <FormattedMessage id="delete" defaultMessage="Delete" />
          </a>
        ),
        <a
          key="configure"
          onClick={() => {
            setConfigureModalVisible(true);
            setCurrentRow(record);
          }}
        >
          {intl.formatMessage({
            id: 'configure',
            defaultMessage: 'Configure',
          })}
        </a>,
      ],
    },
  ];

  return (
    <PageContainer>
      <ProTable<API.ItemData, API.PageParams>
        headerTitle={intl.formatMessage({ id: 'list' })}
        actionRef={actionRef}
        rowKey="_id"
        scroll={{ x: 1200 }}
        search={{
          labelWidth: 120,
          collapsed: false,
        }}
        toolBarRender={() => [
          <Button
            type="primary"
            key="primary"
            onClick={() => {
              handleModalOpen(true);
            }}
          >
            <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="New" />
          </Button>,
          selectedRowsState?.length > 0 && access.canSuperAdmin && (
            <Button
              danger
              onClick={() => {
                return Modal.confirm({
                  title: intl.formatMessage({ id: 'confirm.delete' }),
                  onOk: async () => {
                    await handleRemove(selectedRowsState?.map((item) => item._id!));
                    setSelectedRows([]);
                    actionRef.current?.reloadAndRest?.();
                  },
                  content: intl.formatMessage({ id: 'confirm.delete.content' }),
                  okText: intl.formatMessage({ id: 'confirm' }),
                  cancelText: intl.formatMessage({ id: 'cancel' }),
                });
              }}
            >
              <FormattedMessage
                id="pages.searchTable.batchDeletion"
                defaultMessage="Batch deletion"
              />
            </Button>
          ),
        ]}
        toolbar={{
          menu: {
            type: 'tab',
            activeKey: activeKey,
            items: [
              {
                label: <FormattedMessage id="platform.all" defaultMessage="All" />,
                key: '',
              },
              {
                label: <FormattedMessage id="platform.online" defaultMessage="Online" />,
                key: 'true',
              },
              {
                label: <FormattedMessage id="platform.offline" defaultMessage="Offline" />,
                key: 'false',
              },
            ],
            onChange: (key: any) => {
              setActiveKey(key);
              if (actionRef.current) {
                actionRef.current.reload();
              }
            },
          },
        }}
        request={async (params, sort, filter) =>
          queryList('/platforms', { ...params, isOnline: activeKey }, sort, filter)
        }
        columns={columns}
        rowSelection={{
          onChange: (_, selectedRows) => {
            setSelectedRows(selectedRows);
          },
        }}
      />
      {selectedRowsState?.length > 0 && (
        <FooterToolbar
          extra={
            <div>
              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="Chosen" />{' '}
              <a style={{ fontWeight: 600 }}>{selectedRowsState.length}</a>{' '}
              <FormattedMessage id="pages_searchTable_item" defaultMessage="items" />
            </div>
          }
        ></FooterToolbar>
      )}
      <Create
        open={createModalOpen}
        onOpenChange={handleModalOpen}
        onFinish={async (value) => {
          const success = await handleAdd(value as API.ItemData);
          if (success) {
            handleModalOpen(false);
            if (actionRef.current) {
              actionRef.current.reload();
            }
          }
        }}
      />
      <Update
        onSubmit={async (value) => {
          const success = await handleUpdate(value);
          if (success) {
            handleUpdateModalOpen(false);
            setCurrentRow(undefined);
            if (actionRef.current) {
              actionRef.current.reload();
            }
          }
        }}
        onCancel={handleUpdateModalOpen}
        updateModalOpen={updateModalOpen}
        values={currentRow || {}}
      />

      <ConfigureForm
        onSubmit={async (value) => {
          const success = await handleUpdate(value);
          if (success) {
            setConfigureModalVisible(false);
            setCurrentRow(undefined);
            if (actionRef.current) {
              actionRef.current.reload();
            }
          }
        }}
        onCancel={setConfigureModalVisible}
        updateModalOpen={configureModalVisible}
        values={currentRow || {}}
      />

      <Show
        open={showDetail}
        currentRow={currentRow as API.ItemData}
        columns={columns as ProDescriptionsItemProps<API.ItemData>[]}
        onClose={() => {
          setCurrentRow(undefined);
          setShowDetail(false);
        }}
      />
    </PageContainer>
  );
};

export default TableList;

主要是上面的 menu 的选项处理,还有

const [activeKey, setActiveKey] = useState<string | undefined>('');

然后是请求列表里比较重要,

要把 activeKey 传给后端

复制代码
    request={async (params, sort, filter) =>
      queryList('/platforms', { ...params, isOnline: activeKey }, sort, filter)
    }

后端的参数是叫 isOnline ,要对上 key 和 value

其它的都没啥的问题。

我们拥有 12 年建站编程经验

  1. 虚拟产品交易平台定制开发
  2. WordPress 外贸电商独立站建站

我的网站

相关推荐
L耀早睡8 分钟前
mapreduce打包运行
大数据·前端·spark·mapreduce
咖啡の猫9 分钟前
JavaScript基础-创建对象的三种方式
开发语言·javascript·ecmascript
HouGISer21 分钟前
副业小程序YUERGS,从开发到变现
前端·小程序
outstanding木槿27 分钟前
react中安装依赖时的问题 【集合】
前端·javascript·react.js·node.js
小吕学编程1 小时前
Jackson使用详解
java·javascript·数据库·json
霸王蟹1 小时前
React中useState中更新是同步的还是异步的?
前端·javascript·笔记·学习·react.js·前端框架
霸王蟹1 小时前
React Hooks 必须在组件最顶层调用的原因解析
前端·javascript·笔记·学习·react.js
专注VB编程开发20年1 小时前
asp.net IHttpHandler 对分块传输编码的支持,IIs web服务器后端技术
服务器·前端·asp.net
爱分享的程序员2 小时前
全栈项目搭建指南:Nuxt.js + Node.js + MongoDB
前端
听吉米讲故事2 小时前
Slidev集成Chart.js:专业数据可视化演示文稿优化指南
javascript·信息可视化·数据分析