【图文并茂】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…...
OpenClaw学习路径:从nanobot镜像入门到开发自定义技能
OpenClaw学习路径:从nanobot镜像入门到开发自定义技能 1. 为什么选择OpenClaw作为自动化助手 第一次听说OpenClaw时,我正在为重复性的文件整理工作头疼。作为一个经常需要处理大量技术文档的开发者,每天要花费数小时在机械的文件分类、重命…...
Ubuntu系统身份标识重塑:主机名与用户名的安全变更指南
1. 为什么要修改Ubuntu的主机名和用户名? 很多朋友第一次接触Ubuntu系统时,安装过程中随手设置的主机名和用户名,可能没想到后续会带来这么多麻烦。我遇到过不少这样的情况:公司服务器的主机名还是默认的"ubuntu"&#…...
Markdown到PowerPoint转换技术:md2pptx的架构创新与工程实践
Markdown到PowerPoint转换技术:md2pptx的架构创新与工程实践 【免费下载链接】md2pptx Markdown To PowerPoint converter 项目地址: https://gitcode.com/gh_mirrors/md/md2pptx 在技术文档创作和演示文稿制作领域,传统工作流存在显著效率瓶颈。…...
AI驱动的Vue3应用开发平台深入探究(十):物料系统之内置组件库
内置组件库(Element Plus、Ant Design Vue、Vant) VTJ 通过其统一的物料系统架构,与三个流行的 Vue 组件库提供了全面的集成。这一抽象层使开发者能够利用熟悉的组件模式,同时保持低代码的可扩展性和跨库的可移植性。该系统将组件…...
macOS专属方案:OpenClaw+nanobot镜像的5个效率技巧
macOS专属方案:OpenClawnanobot镜像的5个效率技巧 1. 为什么选择OpenClawnanobot组合 作为一个长期使用macOS的开发者,我一直在寻找能够提升日常工作效率的自动化工具。直到遇到OpenClaw和nanobot这个组合,才真正找到了适合个人使用的智能助…...
高精度运放在电流传感器中的设计与应用
高精度运算放大器在电流传感器中的应用设计1. 电流传感器概述1.1 电流传感器类型与特性电流传感器是用于测量电路电流的关键元件,根据测量原理主要分为以下几种类型:传感器类型测量范围典型应用场景分流电阻式μA~100A电池监测、电机控制磁感应式10mA~1k…...
告别PDF编辑难题:pdf2docx智能转换工具深度解析
告别PDF编辑难题:pdf2docx智能转换工具深度解析 【免费下载链接】pdf2docx Open source Python library converting pdf to docx. 项目地址: https://gitcode.com/gh_mirrors/pd/pdf2docx 还在为无法编辑PDF文档而烦恼吗?是否遇到过需要修改PDF内…...
从标准到实战:网络变压器在POE应用中的AF/AT/BF/BT详解与电路设计指南
1. 网络变压器在POE系统中的核心作用 第一次接触POE供电系统时,我对着电路板上那个带铁壳的方形元件研究了半天——这就是网络变压器。它看起来平平无奇,却是整个POE系统的"心脏"。简单来说,网络变压器在POE系统中要同时干两件事&a…...
如何用浏览器扩展将网页内容一键转换为AI知识库
如何用浏览器扩展将网页内容一键转换为AI知识库 【免费下载链接】anything-llm 这是一个全栈应用程序,可以将任何文档、资源(如网址链接、音频、视频)或内容片段转换为上下文,以便任何大语言模型(LLM)在聊天…...
告别纸上谈兵:用Wireshark抓包实战分析FlexRay帧格式(含CRC校验)
实战解析FlexRay帧格式:用Wireshark抓包验证CRC与网络管理向量 车载工程师们常遇到这样的困境:明明熟读FlexRay协议文档,面对真实总线数据时却无从下手。本文将带您用Wireshark完成从抓包到解析的全流程实战,重点破解Header CRC校…...
