【图文并茂】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,
};
后端用的同一个接口
前端
删除一个

<DeleteLinkonOk={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 (<akey="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(<FormattedMessageid="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 || {}),},});
}
删除多个

<FooterToolbarextra={<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) && (<DeleteButtononOk={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 (<ButtondangeronClick={() => {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(<FormattedMessageid="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 signatureconst 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 (<aonClick={() => {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 && (<akey="edit"onClick={() => {// Replace `handleUpdateModalOpen` and `setCurrentRow` with your actual functionshandleUpdateModalOpen(true);setCurrentRow(record);}}>{intl.formatMessage({ id: 'edit' })}</a>),access.canSuperAdmin && (<DeleteLinkonOk={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) && (<Buttontype="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 && (<FooterToolbarextra={<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) && (<DeleteButtononOk={async () => {await handleRemove(selectedRowsState?.map((item: any) => item._id!));setSelectedRows([]);actionRef.current?.reloadAndRest?.();}}/>)}</FooterToolbar>)}{(access.canSuperAdmin || access.canCreateRole) && (<Createopen={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) && (<UpdateonSubmit={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 || {}}/>)}<Showopen={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 真实案例技术赚钱营销课视频教程
相关文章:
【图文并茂】ant design pro 如何优雅地把删除和批量删除功能合并到一起,并抽出来成为组件
如上图所示,其实批量删除和删除应该算是一个功能 只是删除一个或多个的区别 那么接口应该用的同一个 删除一个的时候呢,就传 一个对象_id 过去 删除多个的时候,就传 多个 对象 _id 的数组过去 后端 const deleteMultipleRoles handleAs…...
监控篇之利用dcgm-exporter监控GPU指标并集成grafana大盘
一、应用场景 当环境中包含GPU节点时,需要了解GPU应用使用节点GPU资源的情况,例如GPU利用率、显存使用量、GPU运行的温度、GPU的功率等。 在获取GPU监控指标后,用户可根据应用的GPU指标配置弹性伸缩策略,或者根据GPU指标设置告警…...
获取当前路由器的外网IP(WAN IP)
GPT-4o (OpenAI) 获取当前路由器的外网IP(WAN IP)可以通过以下几种方法: 1. 访问路由器管理页面: - 通常路由器的管理页面可以通过在浏览器中输入路由器的IP地址来访问(例如,192.168.0.1 或 192.168.1…...
QT Creator UI中文输入跳出英文
笔者用的是QQ拼音输入,发现只要在UI中加入了QTableWidget,输入多几次中文,就会跳入英文。 后面改用搜狗拼音稍微好一些,但是偶尔还是插入了空格。...
Java基础核心知识学习笔记
方法重载 请记住下面重载的条件 方法名称必须相同。参数列表必须不同(个数不同、或类型不同、参数类型排列顺序不同等)。方法的返回类型可以相同也可以不相同。仅仅返回类型不同不足以成为方法的重载。重载是发生在编译时的,因为编译器可以根…...
Leetcode 237.19.83.82 删除链表重复结点 C++实现
Leetcode 237. 删除链表中的节点 问题:有一个单链表的head,我们想删除它其中的一个节点node。给你一个需要删除的节点 node 。你将 无法访问 第一个节点head。链表的所有值都是唯一的,并且保证给定的节点 node不是链表中的最后一个节点。删除…...
Spring OAuth2.0资源服务源码解析
主要分析spring-security-oauth2-resource-server的源码,介绍OAuth2.0授权码模式下Spring Boot OAuth2资源服务的运行流程,分析其是如何对令牌进行认证的,并展示资源服务配置 代码版本信息 Spring Boot 2.7.10 spring-security-oauth2-resou…...
JavaScript 原型与原型链
原型与原型链 要讨论原型与原型链,就要先了解什么是 构造函数 ,构造函数与普通函数没有太大的区别,使用 new关键字 创建实例对象的函数,就叫做构造函数。 在js中,每一个函数类型的数据都有一个 .prototype 的属性&am…...
Spring Boot实现简单的Oracle数据库操作
使用到的技术: 1. Spring Boot:用于简化Spring应用的开发。 2. Dynamic DataSource:实现动态多数据源的访问和切换 3. Oracle JDBC Driver:与Oracle数据库进行连接和交互。 4. Mybatis-Plus:简化SQL映射和数据库访问。…...
微软发布 Phi-3.5 系列模型,涵盖端侧、多模态、MOE;字节 Seed-ASR:自动识别多语言丨 RTE 开发者日报
开发者朋友们大家好: 这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…...
笔记:Echarts柱状图 实现滚轮条 数据太多
效果👇👇👇 配置:👇 let option {dataZoom: [{type: "slider",show: true,zoomLock: true,start: 0,end: 20,bottom: 60,height: 10,textStyle: {color: "transparent",fontSize: 9,},fillerColo…...
嵌入式学习day17(数据结构)
大纲 数据结构、算法数据结构: 1. 线性表:顺序表、链表(单向链表,单向循环链表,双向链表,双向循环链表)、栈(顺序栈,链式栈)、队列(循…...
网站怎么做敏感词过滤,敏感词过滤的思路和实践
敏感词过滤是一种在网站、应用程序或平台中实现内容审查的技术,用于阻止用户发布包含不适当、非法或不符合政策的内容。我们在实际的网站运营过程中,往往需要担心某些用户发布的内容中包含敏感词汇,这些词汇往往会导致我们的网站被用户举报&a…...
【峟思】如何使用投入式水位计才能确保测量准确性
在水利、环保、工业监测等众多领域,水位测量是一项至关重要的任务,它不仅直接关系到水资源的合理利用与保护,还影响到防洪、供水、排水等多个方面的安全与效率。投入式水位计作为一种常见的水位测量工具,以其结构简单、测量准确、…...
供应链管理系统(SCM) —— 企业物流的智能枢纽
SAP 供应链管理系统以打造数字化和集成化的供应链管理平台为使命,将传统的仓储管理系统、制造执行系统、产品管理系统等软件进行升级和上云管理,为企业提供面向客户、合作伙伴及员工的数字化SCM系统平台。 SAP SCM系统从设计到运维,全面优化供…...
计算机视觉(CV)技术的优势和挑战。
计算机视觉(CV)技术在许多领域中具有广泛的应用,并且具有一些优势和挑战。 优势: 1. 高效性:CV技术能够快速处理大量的图像和视频数据,以实现实时的分析和决策。 2. 自动化:CV技术可以自动化地…...
数据库MySQL多表设计、查询
目录 1.概述 2.一对多 3.一对一 4.多对多 5.多表查询 5.1内连接 5.2外连接 5.3子查询 1.概述 项目开发中,在进行数据库表结构设计时,会根据业务需求及业务模块之间的关系,分析并设计表结构,由于业务之间相互关联,所以各个…...
基于vue框架的北城招聘管理平台题目7lly3(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。
系统程序文件列表 项目功能:用户,企业,企业信息,职位类型,职位信息,简历信息,职位应聘,求职意愿,面试信息,录取信息,实习信息,冻结信息,解冻信息 开题报告内容 基于Vue框架的北城招聘管理平台 开题报告 一、引言 随着互联网的飞速发展和企业对人才需求的不断增…...
详讲C#中如何存储当前项目的设置-超级简单省事
我们在编写软件的时候总有一些配置数据需要保存,比如用户选择的偏好设置,又如软件所用到的数据库文件等。我们有很多中方式都可以保存,比如直接保存在某个文本文件,或者ini文件中,其实最简单的办法是保存在项目的资源文…...
【QT文件操作】---xml文件读取
XML(可扩展标记语言,eXtensible Markup Language)是一种用于存储和传输数据的文本格式,广泛用于配置文件、数据交换和文档表示。XML 文件具有明确的结构和标记,这使得它能够描述复杂的层次结构和数据关系。 xml和html…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
C# 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
HDFS分布式存储 zookeeper
hadoop介绍 狭义上hadoop是指apache的一款开源软件 用java语言实现开源框架,允许使用简单的变成模型跨计算机对大型集群进行分布式处理(1.海量的数据存储 2.海量数据的计算)Hadoop核心组件 hdfs(分布式文件存储系统)&a…...
springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
