【图文并茂】ant design pro 如何优雅地把删除和批量删除功能合并到一起,并抽出来成为组件

在这里插入图片描述
在这里插入图片描述
如上图所示,其实批量删除和删除应该算是一个功能

只是删除一个或多个的区别

那么接口应该用的同一个

删除一个的时候呢,就传 一个对象_id 过去

删除多个的时候,就传 多个 对象 _id 的数组过去

后端

在这里插入图片描述

const deleteMultipleRoles = handleAsync(async (req: Request, res: Response) => {
  const { ids } = req.body;

  await Role.deleteMany({
    _id: { $in: ids },
  }).exec();

  res.json({
    success: true,
    message: `${ids.length} roles deleted successfully`,
  });
});

export {
  getRoles,
  getRoleById,
  addRole,
  updateRole,
  deleteRole,
  deleteMultipleRoles,
};

后端用的同一个接口

前端

删除一个

在这里插入图片描述

  <DeleteLink
            onOk={async () => {
              await handleRemove([record._id!]);
              setSelectedRows([]);
              actionRef.current?.reloadAndRest?.();
            }}
          />

DeleteLink 的代码我封装好了

import { Modal } from 'antd';
import { useIntl } from '@umijs/max';

interface DeleteLinkProps {
  onOk: () => Promise<void>;
}

const DeleteLink: React.FC<DeleteLinkProps> = ({ onOk }) => {
  const intl = useIntl();

  return (
    <a
      key="delete"
      onClick={() => {
        return Modal.confirm({
          title: intl.formatMessage({ id: 'confirm_delete' }),
          onOk,
          content: intl.formatMessage({ id: 'confirm_delete_content' }),
          okText: intl.formatMessage({ id: 'confirm' }),
          cancelText: intl.formatMessage({ id: 'cancel' }),
        });
      }}
    >
      {intl.formatMessage({ id: 'delete' })}
    </a>
  );
};

export default DeleteLink;

请求方法在下在面:

/**
 *  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('/roles', {
      ids,
    });
    hide();
    message.success(
      <FormattedMessage
        id="delete_successful"
        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;
  }
};

还有

export async function removeItem(url: string, options?: { [key: string]: any }) {
  return request<Record<string, any>>(url, {
    method: 'DELETE',
    data: {
      ...(options || {}),
    },
  });
}

删除多个

在这里插入图片描述

<FooterToolbar
          extra={
            <div>
              <FormattedMessage id="pages.searchTable.chosen" defaultMessage="Chosen" />{' '}
              <a style={{ fontWeight: 600 }}>{selectedRowsState.length}</a>{' '}
              <FormattedMessage id="pages.searchTable.item" defaultMessage="项" />
            </div>
          }
        >
          {(access.canSuperAdmin || access.canDeleteRole) && (
            <DeleteButton
              onOk={async () => {
                await handleRemove(selectedRowsState?.map((item: any) => item._id!));
                setSelectedRows([]);
                actionRef.current?.reloadAndRest?.();
              }}
            />
          )}
        </FooterToolbar>

DeleteButton 的代码也封装好的

import { Button, Modal } from 'antd';
import { FormattedMessage, useIntl } from '@umijs/max';

interface DeleteButtonProps {
  onOk: () => Promise<void>;
}

const DeleteButton: React.FC<DeleteButtonProps> = ({ onOk }) => {
  const intl = useIntl();

  return (
    <Button
      danger
      onClick={() => {
        return Modal.confirm({
          title: intl.formatMessage({ id: 'confirm_delete' }),
          onOk: onOk,
          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>
  );
};

export default DeleteButton;

最后 index.tsx 完整代码:

import { useIntl } from '@umijs/max';
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, ProFormText, ProTable } from '@ant-design/pro-components';
import { FormattedMessage, useAccess } from '@umijs/max';
import { Button, message } 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 DeleteButton from '@/components/DeleteButton';
import DeleteLink from '@/components/DeleteLink';

/**
 * @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('/roles', { ...fields });
    hide();
    message.success(<FormattedMessage id="add_successful" defaultMessage="Added successfully" />);
    return true;
  } catch (error: any) {
    hide();
    message.error(
      error?.response?.data?.message ?? (
        <FormattedMessage id="upload_failed" defaultMessage="Upload 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(`/roles/${fields._id}`, fields);
    hide();

    message.success(<FormattedMessage id="update_successful" defaultMessage="Update successful" />);
    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('/roles', {
      ids,
    });
    hide();
    message.success(
      <FormattedMessage
        id="delete_successful"
        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 [batchUploadPriceModalOpen, setBatchUploadPriceModalOpen] = useState<boolean>(false);

  const actionRef = useRef<ActionType>();
  const [currentRow, setCurrentRow] = useState<API.ItemData>();
  const [selectedRowsState, setSelectedRows] = useState<API.ItemData[]>([]);
  const [showDetail, setShowDetail] = useState<boolean>(false);
  const access = useAccess();

  /**
   * @en-US International configuration
   * @zh-CN 国际化配置
   * */
  // Define roles object with index signature

  const columns: ProColumns<API.ItemData>[] = [
    {
      title: intl.formatMessage({ id: 'name' }),
      dataIndex: 'name',
      copyable: true,
      renderFormItem: (item, { ...rest }) => {
        return <ProFormText {...rest} placeholder={intl.formatMessage({ id: 'enter_name' })} />;
      },
      render: (dom, entity) => {
        return (
          <a
            onClick={() => {
              setCurrentRow(entity);
              setShowDetail(true);
            }}
          >
            {dom}
          </a>
        );
      },
    },
    {
      title: intl.formatMessage({ id: 'permissions_list' }),
      dataIndex: 'permissions',
      hideInSearch: true,
      hideInTable: true,
      renderText: (val: { name: string }[]) => {
        return val.map((item) => item.name).join(', ');
      },
    },
    {
      title: <FormattedMessage id="pages.searchTable.titleOption" defaultMessage="Operating" />,
      dataIndex: 'option',
      valueType: 'option',
      render: (_, record) => [
        access.canSuperAdmin && (
          <a
            key="edit"
            onClick={() => {
              // Replace `handleUpdateModalOpen` and `setCurrentRow` with your actual functions
              handleUpdateModalOpen(true);
              setCurrentRow(record);
            }}
          >
            {intl.formatMessage({ id: 'edit' })}
          </a>
        ),
        access.canSuperAdmin && (
          <DeleteLink
            onOk={async () => {
              await handleRemove([record._id!]);
              setSelectedRows([]);
              actionRef.current?.reloadAndRest?.();
            }}
          />
        ),
      ],
    },
  ];

  return (
    <PageContainer>
      <ProTable<API.ItemData, API.PageParams>
        headerTitle={intl.formatMessage({ id: 'list' })}
        actionRef={actionRef}
        rowKey="_id"
        search={{
          labelWidth: 100,
        }}
        toolBarRender={() => [
          (access.canSuperAdmin || access.canUpdateRole) && (
            <Button
              type="primary"
              key="primary"
              onClick={() => {
                handleModalOpen(true);
              }}
            >
              <PlusOutlined /> <FormattedMessage id="pages.searchTable.new" defaultMessage="New" />
            </Button>
          ),
        ]}
        request={async (params, sort, filter) => queryList('/roles', params, sort, filter)}
        columns={columns}
        rowSelection={
          access.canSuperAdmin && {
            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="项" />
            </div>
          }
        >
          {(access.canSuperAdmin || access.canDeleteRole) && (
            <DeleteButton
              onOk={async () => {
                await handleRemove(selectedRowsState?.map((item: any) => item._id!));
                setSelectedRows([]);
                actionRef.current?.reloadAndRest?.();
              }}
            />
          )}
        </FooterToolbar>
      )}
      {(access.canSuperAdmin || access.canCreateRole) && (
        <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();
              }
            }
          }}
        />
      )}
      {(access.canSuperAdmin || access.canUpdateRole) && (
        <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 || {}}
        />
      )}
      <Show
        open={showDetail}
        currentRow={currentRow as API.ItemData}
        columns={columns as ProDescriptionsItemProps<API.ItemData>[]}
        onClose={() => {
          setCurrentRow(undefined);
          setShowDetail(false);
        }}
      />
    </PageContainer>
  );
};

export default TableList;

完结

  • ant design pro 如何去保存颜色
  • ant design pro v6 如何做好角色管理
  • ant design 的 tree 如何作为角色中的权限选择之一
  • ant design 的 tree 如何作为角色中的权限选择之二
  • ant design pro access.ts 是如何控制多角色的权限的
  • ant design pro 中用户的表单如何控制多个角色
  • ant design pro 如何实现动态菜单带上 icon 的
  • ant design pro 的表分层级如何处理
  • ant design pro 如何处理权限管理
  • ant design pro 技巧之自制复制到剪贴板组件
  • ant design pro 技巧之实现列表页多标签
  • 【图文并茂】ant design pro 如何对接登录接口
  • 【图文并茂】ant design pro 如何对接后端个人信息接口
  • 【图文并茂】ant design pro 如何给后端发送 json web token - 请求拦截器的使用
  • 【图文并茂】ant design pro 如何使用 refresh token 可续期 token 来提高用户体验
  • 【图文并茂】ant design pro 如何统一封装好 ProFormSelect 的查询请求
  • 【图文并茂】ant design pro 如何优雅地实现查询列表功能

获取 ant design pro & nodejs & typescript 多角色权限动态菜单管理系统源码
我正在做的程序员赚钱副业 - Shopify 真实案例技术赚钱营销课视频教程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/871974.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软件测试-测试分类

测试分类 按照测试目标测试 界面测试 页面内展示的所有内容/元素都需要测试 参考UI图找不同 功能测试 ​ 如何设计功能测试用例&#xff1f; 参考产品规格说明书进行用例的编写&#xff0c;具体的测试用例需要使用黑盒设计测 试用例的方法&#xff0c;如等价类、边界值、…

嵌入式学习——(Linux高级编程——线程控制)

线程的互斥 一、互斥的重要性 在多线程编程中&#xff0c;互斥机制至关重要。当多个线程同时访问临界资源时&#xff0c;如果没有有效的互斥控制&#xff0c;可能会导致数据不一致、资源竞争等问题。通过互斥锁&#xff0c;可以确保在任何时刻只有一个线程能够访问临界资源&am…

PHPShort轻量级网址缩短程序源码开心版,内含汉化包

需要网址缩短并且想获得更多有关链接点击率和流量的数据分析&#xff0c;那么 PHPShort 可能是一个非常好的选择。PHPShort 是一款高级的 URL 缩短器平台&#xff0c;可以帮助你轻松地缩短链接&#xff0c;并根据受众群体的位置或平台来定位受众。 该程序基于 Laravel 框架编写…

python构建一个web程序

from flask import Flaskapp Flask(__name__)app.route(/) def hello_world():return 欢迎来到我的Python Web程序!if __name__ __main__:app.run(debugTrue)1、安装flask D:\Users\USER\PycharmProjects\pythonProject1\p01>pip install flask WARNING: Ignoring invalid…

多线程中常见问题

1、为什么不建议使用Executors来创建线程池&#xff1f; 除开有可能造成的OOM外&#xff0c;使用Executors来创建线程池也不能自定义线程的名字&#xff0c;不利于排查问题&#xff0c;所以建议是直接使用ThreadPoolExecutor来定义线程池&#xff0c;这样可以灵活控制 2、线程…

2 种方式申请免费 SSL 证书,阿里云 Certbot

如何使用免费的 SSL 证书&#xff0c;有时在项目中需要使用免费的 SSL 证书&#xff0c;Aliyun 提供免费证书&#xff0c;三个月有效期&#xff0c;可以直接在aliyun 申请&#xff0c;搜索 SSL 证书&#xff0c;选择测试证书。 Aliyun 证书需要每三月来来换一次&#xff0c;页…

线程优先级调度

Windows优先级调度算法 系统维护了一个全局的处理器数组KiProcessorBlock&#xff0c;其中每个元素对应于一个处理器的KPRCB对象。其次&#xff0c;另有一个全局变量KiIdleSummary记录了哪些处理器当前是空闲的。所谓一个处理器是空闲的&#xff0c;是指该处理器正在执行空闲循…

根据状态的不同,显示不同的背景颜色

文章目录 前言HTML模板部分JavaScript部分注意&#xff1a;主要差异影响如何处理示例 总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内容&#xff1a; 实现效果&#xff1a; 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 根据给定的状态…

内网安全:跨域攻击

目录 获取域信息 利用域信任密钥获取目标域 利用krbtgt哈希值获取目标域 内网中的域林&#xff1a; 很多大型企业都拥有自己的内网&#xff0c;一般通过域林进行共享资源。根据不同职能区分的部门&#xff0c;从逻辑上以 主域和子域进行区分&#xff0c;以方便统一管理。在…

基于x86 平台opencv的图像采集和seetaface6的图像质量评估功能

目录 一、概述二、环境要求2.1 硬件环境2.2 软件环境三、开发流程3.1 编写测试3.2 配置资源文件3.2 验证功能一、概述 本文档是针对x86 平台opencv的图像采集和seetaface6的图像质量评估功能,opencv通过摄像头采集视频图像,将采集的视频图像送给seetaface6的图像质量评估模块…

大模型学习笔记 - LLM 之 attention 优化

LLM 注意力机制 LLM 注意力机制 1. 注意力机制类型概述2.Group Query Attention3.FlashAttention4. PageAttention 1. 注意力机制类型概述 注意力机制最早来源于Transformer&#xff0c;Transformer中的注意力机制分为2种 Encoder中的 全量注意力机制和 Decoder中的带mask的…

qt处理表格,Qtxlsx库文件的安装以及导入

qt想要处理excel表格的&#xff0c;这个过程中避免不了使用Qtxlsx这个库文件。这几天花了几天时间&#xff0c;终于本地调通了。记录一下。 关于Qtxlsx的使用&#xff0c;大致分为2中方法。 方法一&#xff1a;直接下载对应的xlsx文件&#xff0c;然后在.pro文件中 这种方法是…

《黑神话.悟空》:一场跨越神话与现实的深度探索

《黑神话.悟空》&#xff1a;一场跨越神话与现实的深度探索 在国产游戏日益崛起的今天&#xff0c;《黑神话.悟空》以其独特的剧情、丰富的人物设定和深刻的主题&#xff0c;成为了无数玩家翘首以盼的国产3A大作。这款游戏不仅是一次对传统故事的创新演绎&#xff0c;更是一场对…

《黑神话:悟空》游戏攻略:第一回合打法教程!

《黑神话&#xff1a;悟空》是一款以西游记为背景的动作角色扮演游戏&#xff0c;玩家在游戏中将面对各式各样的强大敌人和BOSS。在游戏的第一回合中&#xff0c;你将遇到牯护院、灵虚子、幽魂等多个BOSS。以下是详细的BOSS打法攻略&#xff0c;帮助你在战斗中游刃有余。你可以…

eNSP 华为三层交换机配置DHCP

华为三层交换机配置DHCP 华为DHCP原理&#xff1a;&#xff08;思科四个都是广播包&#xff09; 1、客户端广播发送DHCP Discover包。用于发现当前局域网中的DHCP服务器。 2、DHCP服务器单播发送DHCP Offer包给客户端。携带分配给客户端的IP地址。 3、客户端广播发送DHCP Resqe…

路径规划 | 灰狼算法+B样条曲线优化无人机三维路径规划(Matlab)

目录 效果一览基本介绍程序设计参考文献 效果一览 基本介绍 灰狼算法B样条曲线优化无人机三维路径规划&#xff08;Matlab&#xff09; 群智能路径规划算法。三维灰狼算法&#xff08;GWO&#xff09;加B样条曲线优化的matlab代码。无人机&#xff08;UAV&#xff09;路径规划…

理解Tomcat的IP绑定与访问控制

在使用Spring Boot开发应用时&#xff0c;内置的Tomcat容器提供了灵活的网络配置选项。特别是&#xff0c;当计算机上有多个网卡时&#xff0c;如何配置server.address属性显得尤为重要。本文将详细探讨不同IP配置对Tomcat服务访问的影响。 多网卡环境下的IP配置 假设你的计算…

入门redis

一、安装redis-py库 打开pycharm 在终端中输入 pip install redis 二、连接到redis服务器 import redis r redis.Redis(hostlocalhost, port6379, db0, decode_responsesTrue)host是 Redis 服务器的主机名或 IP 地址&#xff0c;port是端口号&#xff0c;db是要使用的数据库编…

高并发登录模块

1.配置MySQL5.7服务的一主二从 mycat对mysql8不完全⽀持 1. gtids事务复制 2. 删除/etc/my.cnf 3. 同步data⽂件需要先停⽤mysql服务&#xff0c;删除data⽬录中的 auto.cnf 4. gtid模式以及经典模式都需要锁表 flush tables with read lock; unlock tables; set global.read_…

C++设计模式1:单例模式(懒汉模式和饿汉模式,以及多线程问题处理)

饿汉单例模式 程序还没有主动获取实例对象&#xff0c;该对象就产生了&#xff0c;也就是程序刚开始运行&#xff0c;这个对象就已经初始化了。 class Singleton { public:~Singleton(){std::cout << "~Singleton()" << std::endl;}static Singleton* …