如上图所示,其实批量删除和删除应该算是一个功能
只是删除一个或多个的区别
那么接口应该用的同一个
删除一个的时候呢,就传 一个对象_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 真实案例技术赚钱营销课视频教程