| @@ -16,6 +16,18 @@ export default [ | |||||
| redirect: '/workspace', | redirect: '/workspace', | ||||
| breadcrumb: '工作空间', | breadcrumb: '工作空间', | ||||
| }, | }, | ||||
| { | |||||
| name: 'workspace', | |||||
| path: '/workspace', | |||||
| routes: [ | |||||
| { | |||||
| name: '工作空间', | |||||
| path: '', | |||||
| key: 'workspace', | |||||
| component: './Workspace/index', | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | { | ||||
| path: '/user', | path: '/user', | ||||
| layout: false, | layout: false, | ||||
| @@ -31,23 +43,27 @@ export default [ | |||||
| path: '/account', | path: '/account', | ||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| name: 'acenter', | |||||
| name: '用户中心', | |||||
| path: '/account/center', | path: '/account/center', | ||||
| component: './User/Center', | component: './User/Center', | ||||
| }, | }, | ||||
| { | { | ||||
| name: 'asettings', | |||||
| name: '用户设置', | |||||
| path: '/account/settings', | path: '/account/settings', | ||||
| component: './User/Settings', | component: './User/Settings', | ||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| name: 'datasetPreparation', | |||||
| name: '数据准备', | |||||
| path: '/datasetPreparation', | path: '/datasetPreparation', | ||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| name: 'datasetAnnotation', | |||||
| path: '', | |||||
| redirect: '/datasetPreparation/datasetAnnotation', | |||||
| }, | |||||
| { | |||||
| name: '数据标注', | |||||
| path: 'datasetAnnotation', | path: 'datasetAnnotation', | ||||
| component: './DatasetPreparation/DatasetAnnotation/index', | component: './DatasetPreparation/DatasetAnnotation/index', | ||||
| }, | }, | ||||
| @@ -59,9 +75,29 @@ export default [ | |||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| name: 'pipeline', | |||||
| name: 'developmentEnvironment', | |||||
| path: '/developmentEnvironment', | |||||
| routes: [ | |||||
| { | |||||
| name: '开发环境', | |||||
| path: '', | |||||
| component: './DevelopmentEnvironment/Editor', | |||||
| }, | |||||
| { | |||||
| name: '创建编辑器', | |||||
| path: 'create', | |||||
| component: './DevelopmentEnvironment/Create', | |||||
| }, | |||||
| { | |||||
| name: '编辑器', | |||||
| path: 'editor', | |||||
| component: './DevelopmentEnvironment/Editor', | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| name: '流水线', | |||||
| path: '/pipeline', | path: '/pipeline', | ||||
| breadcrumb: '流水线', | |||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| path: '', | path: '', | ||||
| @@ -75,82 +111,39 @@ export default [ | |||||
| name: '流水线模板', | name: '流水线模板', | ||||
| path: '', | path: '', | ||||
| component: './Pipeline/index', | component: './Pipeline/index', | ||||
| breadcrumb: '流水线模板', | |||||
| }, | }, | ||||
| { | { | ||||
| name: '流水线详情', | name: '流水线详情', | ||||
| path: ':id', | |||||
| path: 'info/:id', | |||||
| component: './Pipeline/editPipeline/index', | component: './Pipeline/editPipeline/index', | ||||
| breadcrumb: '流水线详情', | |||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| name: '实验', | name: '实验', | ||||
| path: 'experiment', | path: 'experiment', | ||||
| breadcrumb: '实验', | |||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| name: '实验', | name: '实验', | ||||
| path: '', | path: '', | ||||
| component: './Experiment/index', | component: './Experiment/index', | ||||
| breadcrumb: '实验', | |||||
| }, | }, | ||||
| { | { | ||||
| name: '实验训练', | |||||
| path: ':workflowId/:id', | |||||
| name: '实验实例', | |||||
| path: 'instance/:workflowId/:id', | |||||
| component: './Experiment/Info/index', | component: './Experiment/Info/index', | ||||
| breadcrumb: '实验训练', | |||||
| }, | }, | ||||
| { | { | ||||
| name: '实验对比', | name: '实验对比', | ||||
| path: 'compare', | path: 'compare', | ||||
| component: './Experiment/Comparison/index', | component: './Experiment/Comparison/index', | ||||
| breadcrumb: '实验对比', | |||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| name: 'developmentEnvironment', | |||||
| path: '/developmentEnvironment', | |||||
| routes: [ | |||||
| { | |||||
| name: '开发环境', | |||||
| path: '', | |||||
| component: './DevelopmentEnvironment/Editor', | |||||
| }, | |||||
| { | |||||
| name: '创建编辑器', | |||||
| path: 'create', | |||||
| component: './DevelopmentEnvironment/Create', | |||||
| }, | |||||
| { | |||||
| name: '编辑器', | |||||
| path: 'editor', | |||||
| component: './DevelopmentEnvironment/Editor', | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| name: 'system', | |||||
| path: '/system', | |||||
| routes: [ | |||||
| { | |||||
| name: '字典数据', | |||||
| path: '/system/dict-data/index/:id', | |||||
| component: './System/DictData', | |||||
| }, | |||||
| { | |||||
| name: '分配用户', | |||||
| path: '/system/role-auth/user/:id', | |||||
| component: './System/Role/authUser', | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| name: 'dataset', | |||||
| name: 'AI资产', | |||||
| path: '/dataset', | path: '/dataset', | ||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| @@ -162,13 +155,13 @@ export default [ | |||||
| path: 'dataset', | path: 'dataset', | ||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| name: '数据集列表', | |||||
| name: '数据集', | |||||
| path: '', | path: '', | ||||
| component: './Dataset/index', | component: './Dataset/index', | ||||
| }, | }, | ||||
| { | { | ||||
| name: '数据集简介', | name: '数据集简介', | ||||
| path: ':id', | |||||
| path: 'info/:id', | |||||
| component: './Dataset/intro', | component: './Dataset/intro', | ||||
| }, | }, | ||||
| ], | ], | ||||
| @@ -178,13 +171,13 @@ export default [ | |||||
| path: 'model', | path: 'model', | ||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| name: '模型列表', | |||||
| name: '模型', | |||||
| path: '', | path: '', | ||||
| component: './Model/index', | component: './Model/index', | ||||
| }, | }, | ||||
| { | { | ||||
| name: '模型简介', | name: '模型简介', | ||||
| path: ':id', | |||||
| path: 'info/:id', | |||||
| component: './Model/intro', | component: './Model/intro', | ||||
| }, | }, | ||||
| ], | ], | ||||
| @@ -194,13 +187,13 @@ export default [ | |||||
| path: 'mirror', | path: 'mirror', | ||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| name: '镜像列表', | |||||
| name: '镜像', | |||||
| path: '', | path: '', | ||||
| component: './Mirror/List', | component: './Mirror/List', | ||||
| }, | }, | ||||
| { | { | ||||
| name: '镜像详情', | name: '镜像详情', | ||||
| path: ':id', | |||||
| path: 'info/:id', | |||||
| component: './Mirror/Info', | component: './Mirror/Info', | ||||
| }, | }, | ||||
| { | { | ||||
| @@ -213,40 +206,28 @@ export default [ | |||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| name: 'workspace', | |||||
| path: '/workspace', | |||||
| routes: [ | |||||
| { | |||||
| name: '工作空间', | |||||
| path: '', | |||||
| key: 'workspace', | |||||
| component: './Workspace/index', | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | |||||
| name: 'modelDeployment', | |||||
| name: '模型部署', | |||||
| path: '/modelDeployment', | path: '/modelDeployment', | ||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| name: '模型列表', | |||||
| name: '模型部署', | |||||
| path: '', | path: '', | ||||
| component: './ModelDeployment/List', | component: './ModelDeployment/List', | ||||
| }, | }, | ||||
| { | { | ||||
| name: '镜像详情', | |||||
| path: ':id', | |||||
| name: '模型部署详情', | |||||
| path: 'info/:id', | |||||
| component: './ModelDeployment/Info', | component: './ModelDeployment/Info', | ||||
| }, | }, | ||||
| { | { | ||||
| name: '创建镜像', | |||||
| name: '创建推理服务', | |||||
| path: 'create', | path: 'create', | ||||
| component: './ModelDeployment/Create', | component: './ModelDeployment/Create', | ||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| name: 'appsDeployment', | |||||
| name: '应用开发', | |||||
| path: '/appsDeployment', | path: '/appsDeployment', | ||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| @@ -258,7 +239,7 @@ export default [ | |||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| name: 'see', | |||||
| name: '监控运维', | |||||
| path: '/see', | path: '/see', | ||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| @@ -270,7 +251,7 @@ export default [ | |||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| name: 'readad', | |||||
| name: '资源', | |||||
| path: '/readad', | path: '/readad', | ||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| @@ -282,7 +263,7 @@ export default [ | |||||
| ], | ], | ||||
| }, | }, | ||||
| { | { | ||||
| name: 'compent', | |||||
| name: '组件', | |||||
| path: '/compent', | path: '/compent', | ||||
| routes: [ | routes: [ | ||||
| { | { | ||||
| @@ -320,6 +301,61 @@ export default [ | |||||
| }, | }, | ||||
| ], | ], | ||||
| }, | }, | ||||
| { | |||||
| name: '系统管理', | |||||
| path: '/system', | |||||
| routes: [ | |||||
| { | |||||
| path: '', | |||||
| redirect: '/system/user', | |||||
| }, | |||||
| { | |||||
| name: '用户管理', | |||||
| path: '/system/user', | |||||
| component: './System/User', | |||||
| }, | |||||
| { | |||||
| name: '角色管理', | |||||
| path: '/system/role', | |||||
| component: './System/Role', | |||||
| }, | |||||
| { | |||||
| name: '定时任务', | |||||
| path: '/system/job', | |||||
| component: './Monitor/Job', | |||||
| }, | |||||
| { | |||||
| name: '菜单管理', | |||||
| path: '/system/menu', | |||||
| component: './System/Menu', | |||||
| }, | |||||
| { | |||||
| name: '部门管理', | |||||
| path: '/system/dept', | |||||
| component: './System/Dept', | |||||
| }, | |||||
| { | |||||
| name: '岗位管理', | |||||
| path: '/system/post', | |||||
| component: './System/Post', | |||||
| }, | |||||
| { | |||||
| name: '字典管理', | |||||
| path: '/system/dict', | |||||
| component: './System/Dict', | |||||
| }, | |||||
| { | |||||
| name: '字典数据', | |||||
| path: '/system/dict-data/index/:id', | |||||
| component: './System/DictData', | |||||
| }, | |||||
| { | |||||
| name: '分配用户', | |||||
| path: '/system/role-auth/user/:id', | |||||
| component: './System/Role/authUser', | |||||
| }, | |||||
| ], | |||||
| }, | |||||
| { | { | ||||
| name: 'docs', | name: 'docs', | ||||
| path: '/docs', | path: '/docs', | ||||
| @@ -131,7 +131,7 @@ function ResourceList( | |||||
| activeTag: dataTag, | activeTag: dataTag, | ||||
| }); | }); | ||||
| const prefix = config.prefix; | const prefix = config.prefix; | ||||
| navigate(`/dataset/${prefix}/${record.id}`); | |||||
| navigate(`/dataset/${prefix}/info/${record.id}`); | |||||
| }; | }; | ||||
| // 分页切换 | // 分页切换 | ||||
| @@ -268,6 +268,7 @@ function EditorList() { | |||||
| total: total, | total: total, | ||||
| showSizeChanger: true, | showSizeChanger: true, | ||||
| showQuickJumper: true, | showQuickJumper: true, | ||||
| showTotal: () => `共${total}条`, | |||||
| }} | }} | ||||
| onChange={handleTableChange} | onChange={handleTableChange} | ||||
| rowKey="id" | rowKey="id" | ||||
| @@ -30,7 +30,7 @@ import { experimentStatusInfo } from './status'; | |||||
| const timerIds = new Map(); | const timerIds = new Map(); | ||||
| function Experiment() { | function Experiment() { | ||||
| const navgite = useNavigate(); | |||||
| const navigate = useNavigate(); | |||||
| const [experimentList, setExperimentList] = useState([]); | const [experimentList, setExperimentList] = useState([]); | ||||
| const [workflowList, setWorkflowList] = useState([]); | const [workflowList, setWorkflowList] = useState([]); | ||||
| const [queryFlow, setQueryFlow] = useState({ | const [queryFlow, setQueryFlow] = useState({ | ||||
| @@ -275,12 +275,12 @@ function Experiment() { | |||||
| // 跳转到流水线 | // 跳转到流水线 | ||||
| const gotoPipeline = (e, record) => { | const gotoPipeline = (e, record) => { | ||||
| e.stopPropagation(); | e.stopPropagation(); | ||||
| navgite({ pathname: `/pipeline/template/${record.workflow_id}` }); | |||||
| navigate({ pathname: `/pipeline/template/info/${record.workflow_id}` }); | |||||
| }; | }; | ||||
| // 跳转到实验实例详情 | // 跳转到实验实例详情 | ||||
| const gotoInstanceInfo = (item, record) => { | const gotoInstanceInfo = (item, record) => { | ||||
| navgite({ pathname: `/pipeline/experiment/${record.workflow_id}/${item.id}` }); | |||||
| navigate({ pathname: `/pipeline/experiment/instance/${record.workflow_id}/${item.id}` }); | |||||
| }; | }; | ||||
| // 处理 TensorBoard 操作 | // 处理 TensorBoard 操作 | ||||
| @@ -327,7 +327,7 @@ function Experiment() { | |||||
| }, | }, | ||||
| ], | ], | ||||
| onClick: ({ key }) => { | onClick: ({ key }) => { | ||||
| navgite(`/pipeline/experiment/compare?type=${key}&id=${experimentId}`); | |||||
| navigate(`/pipeline/experiment/compare?type=${key}&id=${experimentId}`); | |||||
| }, | }, | ||||
| }; | }; | ||||
| }; | }; | ||||
| @@ -125,7 +125,7 @@ function MirrorList() { | |||||
| // 查看详情 | // 查看详情 | ||||
| const toDetail = (record: MirrorData) => { | const toDetail = (record: MirrorData) => { | ||||
| navigate(`/dataset/mirror/${record.id}`); | |||||
| navigate(`/dataset/mirror/info/${record.id}`); | |||||
| setCacheState({ | setCacheState({ | ||||
| activeTab, | activeTab, | ||||
| pagination, | pagination, | ||||
| @@ -16,7 +16,7 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||||
| const gotoExperimentPage = () => { | const gotoExperimentPage = () => { | ||||
| if (data.train_task?.ins_id) { | if (data.train_task?.ins_id) { | ||||
| const { origin } = location; | const { origin } = location; | ||||
| const url = `${origin}/pipeline/experiment/${data.workflow_id}/${data.train_task.ins_id}`; | |||||
| const url = `${origin}/pipeline/experiment/instance/${data.workflow_id}/${data.train_task.ins_id}`; | |||||
| window.open(url, '_blank'); | window.open(url, '_blank'); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -28,7 +28,7 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||||
| if (data.current_model_id === resourceId) { | if (data.current_model_id === resourceId) { | ||||
| onVersionChange?.(data.version); | onVersionChange?.(data.version); | ||||
| } else { | } else { | ||||
| const path = `/dataset/model/${data.current_model_id}?tab=${ResourceInfoTabKeys.Evolution}&version=${data.version}`; | |||||
| const path = `/dataset/model/info/${data.current_model_id}?tab=${ResourceInfoTabKeys.Evolution}&version=${data.version}`; | |||||
| navigate(path); | navigate(path); | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -100,7 +100,7 @@ function ModelInfo({ resourceId, data, onVersionChange }: ModelInfoProps) { | |||||
| function DatasetInfo({ data }: { data: TrainDataset }) { | function DatasetInfo({ data }: { data: TrainDataset }) { | ||||
| const gotoDatasetPage = () => { | const gotoDatasetPage = () => { | ||||
| const { origin } = location; | const { origin } = location; | ||||
| const url = `${origin}/dataset/dataset/${data.dataset_id}?tab=${ResourceInfoTabKeys.Version}&version=${data.dataset_version}`; | |||||
| const url = `${origin}/dataset/dataset/info/${data.dataset_id}?tab=${ResourceInfoTabKeys.Version}&version=${data.dataset_version}`; | |||||
| window.open(url, '_blank'); | window.open(url, '_blank'); | ||||
| }; | }; | ||||
| @@ -162,7 +162,7 @@ function ModelDeployment() { | |||||
| searchStatus, | searchStatus, | ||||
| }); | }); | ||||
| navigate(`/modelDeployment/${record.service_id}`); | |||||
| navigate(`/modelDeployment/info/${record.service_id}`); | |||||
| }; | }; | ||||
| // 分页切换 | // 分页切换 | ||||
| @@ -232,7 +232,7 @@ const JobTableList: React.FC = () => { | |||||
| type="link" | type="link" | ||||
| size="small" | size="small" | ||||
| key="edit" | key="edit" | ||||
| icon=<EditOutlined /> | |||||
| icon={<EditOutlined />} | |||||
| hidden={!access.hasPerms('monitor:job:edit')} | hidden={!access.hasPerms('monitor:job:edit')} | ||||
| onClick={() => { | onClick={() => { | ||||
| setModalVisible(true); | setModalVisible(true); | ||||
| @@ -246,7 +246,7 @@ const JobTableList: React.FC = () => { | |||||
| size="small" | size="small" | ||||
| danger | danger | ||||
| key="batchRemove" | key="batchRemove" | ||||
| icon=<DeleteOutlined /> | |||||
| icon={<DeleteOutlined />} | |||||
| hidden={!access.hasPerms('monitor:job:remove')} | hidden={!access.hasPerms('monitor:job:remove')} | ||||
| onClick={async () => { | onClick={async () => { | ||||
| Modal.confirm({ | Modal.confirm({ | ||||
| @@ -3,7 +3,7 @@ import { DeleteOutlined } from '@ant-design/icons'; | |||||
| import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components'; | import { ActionType, ProColumns, ProTable } from '@ant-design/pro-components'; | ||||
| import { FormattedMessage, useAccess, useIntl } from '@umijs/max'; | import { FormattedMessage, useAccess, useIntl } from '@umijs/max'; | ||||
| import type { FormInstance } from 'antd'; | import type { FormInstance } from 'antd'; | ||||
| import { Button, message, Modal } from 'antd'; | |||||
| import { Button, Modal, message } from 'antd'; | |||||
| import React, { useEffect, useRef } from 'react'; | import React, { useEffect, useRef } from 'react'; | ||||
| /* * | /* * | ||||
| @@ -102,7 +102,7 @@ const OnlineUserTableList: React.FC = () => { | |||||
| size="small" | size="small" | ||||
| danger | danger | ||||
| key="batchRemove" | key="batchRemove" | ||||
| icon=<DeleteOutlined /> | |||||
| icon={<DeleteOutlined />} | |||||
| hidden={!access.hasPerms('monitor:online:forceLogout')} | hidden={!access.hasPerms('monitor:online:forceLogout')} | ||||
| onClick={async () => { | onClick={async () => { | ||||
| Modal.confirm({ | Modal.confirm({ | ||||
| @@ -21,7 +21,7 @@ import Styles from './index.less'; | |||||
| const { TextArea } = Input; | const { TextArea } = Input; | ||||
| const Pipeline = () => { | const Pipeline = () => { | ||||
| const [form] = Form.useForm(); | const [form] = Form.useForm(); | ||||
| const navgite = useNavigate(); | |||||
| const navigate = useNavigate(); | |||||
| const [formId, setFormId] = useState(null); | const [formId, setFormId] = useState(null); | ||||
| const [dialogTitle, setDialogTitle] = useState('新建流水线'); | const [dialogTitle, setDialogTitle] = useState('新建流水线'); | ||||
| @@ -43,7 +43,7 @@ const Pipeline = () => { | |||||
| }; | }; | ||||
| const routeToEdit = (e, record) => { | const routeToEdit = (e, record) => { | ||||
| e.stopPropagation(); | e.stopPropagation(); | ||||
| navgite({ pathname: `/pipeline/template/${record.id}` }); | |||||
| navigate({ pathname: `/pipeline/template/info/${record.id}` }); | |||||
| }; | }; | ||||
| const showModal = () => { | const showModal = () => { | ||||
| form.resetFields(); | form.resetFields(); | ||||
| @@ -66,7 +66,7 @@ const Pipeline = () => { | |||||
| setIsModalOpen(false); | setIsModalOpen(false); | ||||
| message.success('新建成功'); | message.success('新建成功'); | ||||
| if (ret.code === 200) { | if (ret.code === 200) { | ||||
| navgite({ pathname: `/pipeline/template/${ret.data.id}` }); | |||||
| navigate({ pathname: `/pipeline/template/info/${ret.data.id}` }); | |||||
| } | } | ||||
| }); | }); | ||||
| } | } | ||||
| @@ -261,7 +261,7 @@ const RoleTableList: React.FC = () => { | |||||
| type="link" | type="link" | ||||
| size="small" | size="small" | ||||
| key="edit" | key="edit" | ||||
| icon=<EditOutlined /> | |||||
| icon={<EditOutlined />} | |||||
| hidden={!access.hasPerms('system:role:edit')} | hidden={!access.hasPerms('system:role:edit')} | ||||
| onClick={() => { | onClick={() => { | ||||
| getRoleMenuList(record.roleId).then((res) => { | getRoleMenuList(record.roleId).then((res) => { | ||||
| @@ -288,7 +288,7 @@ const RoleTableList: React.FC = () => { | |||||
| size="small" | size="small" | ||||
| danger | danger | ||||
| key="batchRemove" | key="batchRemove" | ||||
| icon=<DeleteOutlined /> | |||||
| icon={<DeleteOutlined />} | |||||
| hidden={!access.hasPerms('system:role:remove')} | hidden={!access.hasPerms('system:role:remove')} | ||||
| onClick={async () => { | onClick={async () => { | ||||
| Modal.confirm({ | Modal.confirm({ | ||||
| @@ -286,7 +286,7 @@ const UserTableList: React.FC = () => { | |||||
| type="link" | type="link" | ||||
| size="small" | size="small" | ||||
| key="edit" | key="edit" | ||||
| icon=<EditOutlined /> | |||||
| icon={<EditOutlined />} | |||||
| hidden={!access.hasPerms('system:user:edit')} | hidden={!access.hasPerms('system:user:edit')} | ||||
| onClick={async () => { | onClick={async () => { | ||||
| fetchUserInfo(record.userId); | fetchUserInfo(record.userId); | ||||
| @@ -302,7 +302,7 @@ const UserTableList: React.FC = () => { | |||||
| type="link" | type="link" | ||||
| size="small" | size="small" | ||||
| danger | danger | ||||
| icon=<DeleteOutlined /> | |||||
| icon={<DeleteOutlined />} | |||||
| key="batchRemove" | key="batchRemove" | ||||
| hidden={!access.hasPerms('system:user:remove')} | hidden={!access.hasPerms('system:user:remove')} | ||||
| onClick={async () => { | onClick={async () => { | ||||