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 外贸电商独立站建站

我的网站

相关推荐
李明卫杭州6 分钟前
JavaScript 严格模式下 arguments 的区别
前端·javascript
swipe9 分钟前
向量数据库实战:为什么 AI Agent 离不开 Milvus
前端·面试·agent
小锋学长生活大爆炸19 分钟前
【教程】Edge浏览器中可以提升性能的flags
前端·edge
苍舒墨22 分钟前
如何借助Github pages部署React+vite静态前端项目
前端
曹牧31 分钟前
JSON 数组的正确使用方式
java·服务器·前端
一次旅行32 分钟前
今日心理学知识分享(三)
开发语言·javascript·程序人生·ecmascript
MobotStone39 分钟前
为什么别人用AI像“开挂”?一文读懂 Claude Skills 2.0 的颠覆性变革
架构·前端框架
小村儿1 小时前
一起吃透 Claude Code,告别 AI 编程迷茫
前端·后端·ai编程
牛十二1 小时前
openclaw安装mcporter搜索小红书
开发语言·javascript·ecmascript
小金鱼Y1 小时前
🔥 前端人必看:浏览器安全核心知识点全解析(XSS/CSRF/DDoS)
前端·javascript·安全