style: ui for no data in table list

This commit is contained in:
jialin
2025-10-16 11:11:39 +08:00
parent ca9ceda2f7
commit e2da7c5f96
29 changed files with 546 additions and 108 deletions
+108
View File
@@ -0,0 +1,108 @@
import { Empty, EmptyProps, Typography } from 'antd';
import _ from 'lodash';
import React, { useMemo } from 'react';
import styled from 'styled-components';
const StyledEmpty = styled(Empty)`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-block: 60px 32px;
.ant-empty-image {
margin-bottom: 0;
height: auto;
line-height: 1;
font-size: 42px;
.anticon {
color: var(--ant-color-primary);
}
}
.ant-empty-footer {
display: flex;
}
`;
const ImageWrapper = styled.div`
margin-bottom: 16px;
display: flex;
justify-content: center;
align-items: center;
`;
const SimpleImageWrapper = styled.div`
margin-bottom: 8px;
display: flex;
justify-content: center;
align-items: center;
`;
const Description = styled.div`
display: flex;
flex-direction: column;
gap: 12px;
justify-content: center;
align-items: center;
`;
const NoResult: React.FC<
EmptyProps & {
title?: React.ReactNode;
subTitle?: React.ReactNode;
noFoundText?: React.ReactNode;
filters?: Record<string, any>;
loading?: boolean;
loadend?: boolean;
dataSource?: any[];
}
> = (props) => {
const { filters, noFoundText, loadend, loading, dataSource } = props;
const hasFilters = useMemo(() => {
const filterValues = _.omit(filters, ['page', 'perPage']);
return Object.values(filterValues || {}).some((value) => {
if (Array.isArray(value)) {
return value.length > 0;
}
return !!value;
});
}, [filters]);
return (
<>
{!loading && loadend && !dataSource?.length ? (
<StyledEmpty
image={
hasFilters ? (
<SimpleImageWrapper>
{Empty.PRESENTED_IMAGE_SIMPLE}
</SimpleImageWrapper>
) : (
<ImageWrapper>{props.image}</ImageWrapper>
)
}
description={
<Description>
{!hasFilters && (
<Typography.Text style={{ fontSize: '18px', fontWeight: 500 }}>
{props.title}
</Typography.Text>
)}
<Typography.Text type="secondary">
{hasFilters ? noFoundText : props.subTitle}
</Typography.Text>
</Description>
}
>
{!hasFilters && props.children}
</StyledEmpty>
) : (
<span></span>
)}
</>
);
};
export default NoResult;
+21 -9
View File
@@ -1,4 +1,5 @@
import DeleteModal from '@/components/delete-modal';
import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools';
import { PageAction } from '@/config';
import type { PageActionType } from '@/config/types';
@@ -6,8 +7,9 @@ import useTableFetch from '@/hooks/use-table-fetch';
import { PageContainer } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max';
import useMemoizedFn from 'ahooks/lib/useMemoizedFn';
import { ConfigProvider, Empty, Table } from 'antd';
import { Button, ConfigProvider, Table } from 'antd';
import { useState } from 'react';
import NoResult from '../_components/no-result';
import { deleteApisKey, queryApisKeysList } from './apis';
import AddAPIKeyModal from './components/add-apikey-modal';
import { ListItem } from './config/types';
@@ -97,14 +99,24 @@ const APIKeys: React.FC = () => {
const renderEmpty = (type?: string) => {
if (type !== 'Table') return;
if (
!dataSource.loading &&
dataSource.loadend &&
!dataSource.dataList.length
) {
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty>;
}
return <div></div>;
return (
<NoResult
loading={dataSource.loading}
loadend={dataSource.loadend}
dataSource={dataSource.dataList}
image={<IconFont type="icon-key" />}
filters={queryParams}
noFoundText={intl.formatMessage({
id: 'noresult.keys.nofound'
})}
title={intl.formatMessage({ id: 'noresult.keys.title' })}
subTitle={intl.formatMessage({ id: 'noresult.keys.subTitle' })}
>
<Button type="primary" onClick={handleAddKey}>
{intl.formatMessage({ id: 'noresult.button.add' })}
</Button>
</NoResult>
);
};
const columns = useKeysColumns({ handleSelect: onSelect, sortOrder });
+24 -2
View File
@@ -1,13 +1,16 @@
import DeleteModal from '@/components/delete-modal';
import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools';
import { PageActionType } from '@/config/types';
import useTableFetch from '@/hooks/use-table-fetch';
import { PageContainer } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max';
import useMemoizedFn from 'ahooks/lib/useMemoizedFn';
import { Button } from 'antd';
import _ from 'lodash';
import { useState } from 'react';
import { ScrollerContext } from '../_components/infinite-scroller/use-scroller-context';
import NoResult from '../_components/no-result';
import {
createBackend,
createBackendFromYAML,
@@ -154,8 +157,11 @@ const BackendList = () => {
const loadMore = useMemoizedFn((nextPage: number) => {
fetchData({
...queryParams,
page: nextPage
query: {
...queryParams,
page: nextPage
},
loadmore: true
});
});
@@ -204,6 +210,22 @@ const BackendList = () => {
isFirst={!dataSource.loadend}
onSelect={handleOnSelect}
></BackendCardList>
<NoResult
loading={dataSource.loading}
loadend={dataSource.loadend}
dataSource={dataSource.dataList}
image={<IconFont type="icon-models" />}
filters={queryParams}
noFoundText={intl.formatMessage({
id: 'noresult.backend.nofound'
})}
title={intl.formatMessage({ id: 'noresult.backend.title' })}
subTitle={intl.formatMessage({ id: 'noresult.backend.subTitle' })}
>
<Button type="primary" onClick={handleAddBackend}>
{intl.formatMessage({ id: 'noresult.button.add' })}
</Button>
</NoResult>
</ScrollerContext.Provider>
<AddModal
action={openModalStatus.action}
+25 -3
View File
@@ -1,5 +1,6 @@
import { expandKeysAtom } from '@/atoms/clusters';
import DeleteModal from '@/components/delete-modal';
import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools';
import SealTable from '@/components/seal-table';
import TableContext from '@/components/seal-table/table-context';
@@ -12,9 +13,10 @@ import AddWorker from '@/pages/cluster-management/components/add-worker';
import { PageContainer } from '@ant-design/pro-components';
import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
import { useMemoizedFn } from 'ahooks';
import { message } from 'antd';
import { Button, message } from 'antd';
import { useAtom } from 'jotai';
import { useEffect, useState } from 'react';
import NoResult from '../_components/no-result';
import {
CLUSTERS_API,
createWorkerPool,
@@ -39,7 +41,7 @@ import {
} from './config/types';
import useClusterColumns from './hooks/use-cluster-columns';
const Credentials: React.FC = () => {
const Clusters: React.FC = () => {
const {
dataSource,
rowSelection,
@@ -372,6 +374,26 @@ const Credentials: React.FC = () => {
columns={columns}
childParentKey="cluster_id"
expandable={true}
empty={
<NoResult
loading={dataSource.loading}
loadend={dataSource.loadend}
dataSource={dataSource.dataList}
image={<IconFont type="icon-cluster-outline" />}
filters={queryParams}
noFoundText={intl.formatMessage({
id: 'noresult.cluster.nofound'
})}
title={intl.formatMessage({ id: 'noresult.cluster.title' })}
subTitle={intl.formatMessage({
id: 'noresult.cluster.subTitle'
})}
>
<Button type="primary" onClick={handleClickDropdown}>
{intl.formatMessage({ id: 'noresult.button.add' })}
</Button>
</NoResult>
}
pagination={{
showSizeChanger: true,
pageSize: queryParams.perPage,
@@ -451,4 +473,4 @@ const Credentials: React.FC = () => {
);
};
export default Credentials;
export default Clusters;
+25 -9
View File
@@ -8,9 +8,10 @@ import useTableFetch from '@/hooks/use-table-fetch';
import { PageContainer } from '@ant-design/pro-components';
import { useIntl, useNavigate } from '@umijs/max';
import { useMemoizedFn } from 'ahooks';
import { ConfigProvider, Empty, Table, message } from 'antd';
import { Button, ConfigProvider, Table, message } from 'antd';
import { useAtom } from 'jotai';
import { useState } from 'react';
import NoResult from '../_components/no-result';
import {
createCredential,
deleteCredential,
@@ -148,14 +149,29 @@ const Credentials: React.FC = () => {
const renderEmpty = (type?: string) => {
if (type !== 'Table') return;
if (
!dataSource.loading &&
dataSource.loadend &&
!dataSource.dataList.length
) {
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty>;
}
return <div></div>;
return (
<NoResult
loading={dataSource.loading}
loadend={dataSource.loadend}
dataSource={[]}
image={<IconFont type="icon-credential-outline" />}
filters={queryParams}
noFoundText={intl.formatMessage({
id: 'noresult.credentials.nofound'
})}
title={intl.formatMessage({ id: 'noresult.credentials.title' })}
subTitle={intl.formatMessage({
id: 'noresult.credentials.subTitle'
})}
>
<Button
type="primary"
onClick={() => handleAddCredential(addActions[0])}
>
{intl.formatMessage({ id: 'noresult.button.add' })}
</Button>
</NoResult>
);
};
const columns = useCredentialColumns(sortOrder, handleSelect);
+2 -8
View File
@@ -5,6 +5,7 @@ import { request } from '@umijs/max';
import qs from 'query-string';
import {
AccessControlFormData,
BackendItem,
CatalogItem,
CatalogSpec,
EvaluateResult,
@@ -405,14 +406,7 @@ export async function evaluationsModelSpec(
export async function queryBackendList(params?: { cluster_id: number }) {
return request<{
items: {
backend_name: string;
backend_show_name: string;
from_config: boolean;
default_version: string;
default_backend_param: string[];
versions: string[];
}[];
items: BackendItem[];
}>(BACKEND_LIST_API, {
method: 'GET',
params
+14
View File
@@ -1,4 +1,5 @@
import { modelsExpandKeysAtom } from '@/atoms/models';
import IconFont from '@/components/icon-font';
import PageTools from '@/components/page-tools';
import BaseSelect from '@/components/seal-form/base/select';
import { PageAction } from '@/config';
@@ -14,6 +15,7 @@ import { useAtom } from 'jotai';
import _ from 'lodash';
import React, { useCallback, useEffect, useState } from 'react';
import styled from 'styled-components';
import NoResult from '../_components/no-result';
import { createModel, queryCatalogList } from './apis';
import CatalogList from './components/catalog-list';
import DelopyBuiltInModal from './components/deploy-builtin-modal';
@@ -297,6 +299,18 @@ const Catalog: React.FC = () => {
activeId={-1}
isFirst={isFirst}
></CatalogList>
<NoResult
loading={dataSource.loading}
loadend={!isFirst}
dataSource={dataSource.dataList}
image={<IconFont type="icon-layers" />}
filters={queryParams}
noFoundText={intl.formatMessage({
id: 'noresult.catalog.nofound'
})}
title={intl.formatMessage({ id: 'noresult.catalog.title' })}
subTitle={intl.formatMessage({ id: 'noresult.catalog.subTitle' })}
></NoResult>
</ScrollerContext.Provider>
<PageWrapper>
<Pagination
@@ -2,6 +2,7 @@ import CatalogSkelton from '@/components/templates/card-skelton';
import breakpoints from '@/config/breakpoints';
import InfiniteScroller from '@/pages/_components/infinite-scroller';
import { useScrollerContext } from '@/pages/_components/infinite-scroller/use-scroller-context';
import { useIntl } from '@umijs/max';
import { Col, FloatButton, Row, Spin } from 'antd';
import _ from 'lodash';
import ResizeObserver from 'rc-resize-observer';
@@ -55,6 +56,7 @@ const ListSkeleton: React.FC<{
};
const CatalogList: React.FC<CatalogListProps> = (props) => {
const intl = useIntl();
const { dataList, loading, activeId, isFirst, onDeploy } = props;
const {
total,
@@ -2,6 +2,7 @@ import { modelsExpandKeysAtom } from '@/atoms/models';
import DeleteModal from '@/components/delete-modal';
import DropDownActions from '@/components/drop-down-actions';
import DropdownButtons from '@/components/drop-down-buttons';
import IconFont from '@/components/icon-font';
import { PageSize } from '@/components/logs-viewer/config';
import PageTools from '@/components/page-tools';
import BaseSelect from '@/components/seal-form/base/select';
@@ -12,6 +13,7 @@ import useBodyScroll from '@/hooks/use-body-scroll';
import useExpandedRowKeys from '@/hooks/use-expanded-row-keys';
import useTableRowSelection from '@/hooks/use-table-row-selection';
import useTableSort from '@/hooks/use-table-sort';
import NoResult from '@/pages/_components/no-result';
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
import { handleBatchRequest } from '@/utils';
import { DownOutlined, SearchOutlined, SyncOutlined } from '@ant-design/icons';
@@ -704,6 +706,30 @@ const Models: React.FC<ModelsProps> = ({
loadChildren={getModelInstances}
loadChildrenAPI={generateChildrenRequestAPI}
renderChildren={renderChildren}
empty={
<NoResult
loading={loading}
loadend={loadend}
dataSource={dataSource}
image={<IconFont type="icon-rocket-launch1" />}
filters={queryParams}
noFoundText={intl.formatMessage({
id: 'noresult.mymodels.nofound'
})}
title={intl.formatMessage({ id: 'noresult.deployments.title' })}
subTitle={intl.formatMessage({
id: 'noresult.deployments.subTitle'
})}
>
<Button
type="primary"
iconPosition="end"
onClick={() => handleClickDropdown({ key: 'catalog' })}
>
{intl?.formatMessage?.({ id: 'models.table.button.deploy' })}
</Button>
</NoResult>
}
pagination={{
showSizeChanger: true,
pageSize: queryParams.perPage,
@@ -5,7 +5,7 @@ import vllmParameters from './vllm-parameters';
export const backendOptionsMap = {
llamaBox: 'llama-box',
vllm: 'vLLM',
voxBox: 'vox-box',
voxBox: 'VoxBox',
ascendMindie: 'MindIE',
custom: 'Custom'
};
@@ -13,12 +13,6 @@ export const modalConfig: Record<
width: 'calc(100vw - 220px)',
source: modelSourceMap.huggingface_value
},
ollama_library: {
show: true,
width: 600,
isGGUF: true,
source: modelSourceMap.ollama_library_value
},
modelscope: {
show: true,
width: 'calc(100vw - 220px)',
+11
View File
@@ -264,3 +264,14 @@ export interface AccessControlFormData {
access_policy: 'public' | 'authed' | 'allowed_users';
users: { id: number }[];
}
export interface BackendItem {
backend_name: string;
from_config: boolean;
default_version: string;
default_backend_param: string[];
versions: {
version: string;
is_deprecated: boolean;
}[];
}
@@ -11,12 +11,12 @@ export default function useQueryBackends() {
const list = res?.items?.map((item) => {
return {
value: item.backend_name,
label: item.backend_show_name,
label: item.backend_name,
default_backend_param: item.default_backend_param || [],
default_version: item.default_version,
versions: (item.versions || []).map((version) => ({
label: version,
value: version
versions: (item.versions || []).map((vItem) => ({
label: vItem.version,
value: vItem.version
}))
};
});
+11 -3
View File
@@ -44,6 +44,7 @@ const Models: React.FC = () => {
page: 1,
perPage: 10,
search: '',
cluster_id: 0,
categories: []
});
@@ -191,7 +192,11 @@ const Models: React.FC = () => {
};
const createModelsChunkRequest = useCallback(
async (params?: { search: string; categories: any[] }) => {
async (params?: {
search: string;
categories: any[];
cluster_id: number;
}) => {
const search = params?.search || queryParams.search;
const categories = params?.categories || queryParams.categories;
chunkRequedtRef.current?.current?.cancel?.();
@@ -266,7 +271,8 @@ const Models: React.FC = () => {
});
createModelsChunkRequest({
search: e.target.value,
categories: queryParams.categories
categories: queryParams.categories,
cluster_id: queryParams.cluster_id || 0
});
}, 350);
@@ -279,17 +285,19 @@ const Models: React.FC = () => {
});
createModelsChunkRequest({
search: queryParams.search,
cluster_id: queryParams.cluster_id || 0,
categories: value
});
};
const handleClusterChange = async (value: any) => {
const handleClusterChange = async (value: number) => {
handleQueryChange({
page: 1,
cluster_id: value
});
createModelsChunkRequest({
search: queryParams.search,
categories: queryParams.categories,
cluster_id: value
});
};
+19 -2
View File
@@ -1,3 +1,4 @@
import IconFont from '@/components/icon-font';
import PageTools from '@/components/page-tools';
import BaseSelect from '@/components/seal-form/base/select';
import CardList from '@/components/templates/card-list';
@@ -10,6 +11,7 @@ import useMemoizedFn from 'ahooks/lib/useMemoizedFn';
import { Button, Input, Space } from 'antd';
import React from 'react';
import { ScrollerContext } from '../_components/infinite-scroller/use-scroller-context';
import NoResult from '../_components/no-result';
import { MY_MODELS_API, queryMyModels } from './apis';
import ModelItem from './components/model-item';
import { categoryOptions, modelCategoriesMap } from './config';
@@ -67,8 +69,11 @@ const UserModels: React.FC = () => {
const loadMore = useMemoizedFn((nextPage: number) => {
fetchData({
...queryParams,
page: nextPage
query: {
...queryParams,
page: nextPage
},
loadmore: true
});
});
@@ -137,6 +142,18 @@ const UserModels: React.FC = () => {
Skeleton={CardSkeleton}
renderItem={renderCard}
></CardList>
<NoResult
loading={dataSource.loading}
loadend={dataSource.loadend}
dataSource={dataSource.dataList}
image={<IconFont type="icon-models" />}
filters={queryParams}
noFoundText={intl.formatMessage({
id: 'noresult.mymodels.nofound'
})}
title={intl.formatMessage({ id: 'noresult.mymodels.title' })}
subTitle={intl.formatMessage({ id: 'noresult.mymodels.subTitle' })}
></NoResult>
</ScrollerContext.Provider>
</PageContainer>
);
+15 -9
View File
@@ -1,9 +1,11 @@
import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools';
import useTableFetch from '@/hooks/use-table-fetch';
import NoResult from '@/pages/_components/no-result';
import { queryClusterList } from '@/pages/cluster-management/apis';
import { PageContainer } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max';
import { ConfigProvider, Empty, Table } from 'antd';
import { ConfigProvider, Table } from 'antd';
import React, { useEffect, useState } from 'react';
import { GPU_DEVICES_API, queryGpuDevicesList } from '../apis';
import { GPUDeviceItem } from '../config/types';
@@ -44,14 +46,18 @@ const GPUList: React.FC = () => {
const renderEmpty = (type?: string) => {
if (type !== 'Table') return;
if (
!dataSource.loading &&
dataSource.loadend &&
!dataSource.dataList.length
) {
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty>;
}
return <div></div>;
return (
<NoResult
loading={dataSource.loading}
loadend={dataSource.loadend}
dataSource={dataSource.dataList}
image={<IconFont type="icon-gpu1" />}
filters={queryParams}
noFoundText={intl.formatMessage({ id: 'noresult.gpus.nofound' })}
title={intl.formatMessage({ id: 'noresult.gpus.title' })}
subTitle={intl.formatMessage({ id: 'noresult.gpus.subTitle' })}
></NoResult>
);
};
const columns = useGPUColumns({
+19 -13
View File
@@ -1,10 +1,12 @@
import { modelsExpandKeysAtom } from '@/atoms/models';
import DeleteModal from '@/components/delete-modal';
import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools';
import { PageAction } from '@/config';
import useAppUtils from '@/hooks/use-app-utils';
import useBodyScroll from '@/hooks/use-body-scroll';
import useTableFetch from '@/hooks/use-table-fetch';
import NoResult from '@/pages/_components/no-result';
import { createModel } from '@/pages/llmodels/apis';
import DeployModal from '@/pages/llmodels/components/deploy-modal';
import { modelSourceMap } from '@/pages/llmodels/config';
@@ -21,7 +23,7 @@ import useRecognizeAudio from '@/pages/llmodels/hooks/use-recognize-audio';
import { PageContainer } from '@ant-design/pro-components';
import { useIntl, useNavigate } from '@umijs/max';
import { useMemoizedFn } from 'ahooks';
import { ConfigProvider, Empty, Table, message } from 'antd';
import { ConfigProvider, Table, message } from 'antd';
import { useAtom } from 'jotai';
import _ from 'lodash';
import { useEffect, useMemo, useState } from 'react';
@@ -190,18 +192,6 @@ const ModelFiles = () => {
}
});
const renderEmpty = (type?: string) => {
if (type !== 'Table') return;
if (
!dataSource.loading &&
dataSource.loadend &&
!dataSource.dataList.length
) {
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty>;
}
return <div></div>;
};
const handleClickDropdown = (item: any) => {
const config = modalConfig[item.key];
const hasLinuxWorker = workersList.some(
@@ -212,6 +202,22 @@ const ModelFiles = () => {
}
};
const renderEmpty = (type?: string) => {
if (type !== 'Table') return;
return (
<NoResult
loading={dataSource.loading}
loadend={dataSource.loadend}
dataSource={dataSource.dataList}
image={<IconFont type="icon-files" />}
filters={queryParams}
noFoundText={intl.formatMessage({ id: 'noresult.modelfiles.nofound' })}
title={intl.formatMessage({ id: 'noresult.modelfiles.title' })}
subTitle={intl.formatMessage({ id: 'noresult.modelfiles.subTitle' })}
></NoResult>
);
};
const handleDownloadCancel = () => {
setDownlaodMoalStatus({
...downloadModalStatus,
+25 -10
View File
@@ -1,12 +1,14 @@
import DeleteModal from '@/components/delete-modal';
import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools';
import useTableFetch from '@/hooks/use-table-fetch';
import { queryClusterList } from '@/pages/cluster-management/apis';
import { PageContainer } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max';
import { useIntl, useNavigate } from '@umijs/max';
import { useMemoizedFn } from 'ahooks';
import { ConfigProvider, Empty, Table, message } from 'antd';
import { Button, ConfigProvider, Table, message } from 'antd';
import React, { useEffect, useState } from 'react';
import NoResult from '../../_components/no-result';
import TerminalTabs from '../../_components/terminal-tabs';
import {
WORKERS_API,
@@ -43,6 +45,7 @@ const Workers: React.FC = () => {
API: WORKERS_API
});
const navigate = useNavigate();
const intl = useIntl();
const [updateLabelsData, setUpdateLabelsData] = useState<{
open: boolean;
@@ -175,16 +178,28 @@ const Workers: React.FC = () => {
}
});
const handleAddWorker = () => {
navigate('/cluster-management/clusters/list');
};
const renderEmpty = (type?: string) => {
if (type !== 'Table') return;
if (
!dataSource.loading &&
dataSource.loadend &&
!dataSource.dataList.length
) {
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty>;
}
return <div></div>;
return (
<NoResult
loading={dataSource.loading}
loadend={dataSource.loadend}
dataSource={dataSource.dataList}
image={<IconFont type="icon-resources" />}
filters={queryParams}
noFoundText={intl.formatMessage({ id: 'noresult.workers.nofound' })}
title={intl.formatMessage({ id: 'noresult.workers.title' })}
subTitle={intl.formatMessage({ id: 'noresult.workers.subTitle' })}
>
<Button type="primary" onClick={handleAddWorker}>
{intl.formatMessage({ id: 'noresult.workers.button.add' })}
</Button>
</NoResult>
);
};
const handleClusterChange = (value: number) => {
+21 -17
View File
@@ -1,4 +1,5 @@
import DeleteModal from '@/components/delete-modal';
import IconFont from '@/components/icon-font';
import { FilterBar } from '@/components/page-tools';
import { PageAction } from '@/config';
import type { PageActionType } from '@/config/types';
@@ -6,21 +7,14 @@ import useTableFetch from '@/hooks/use-table-fetch';
import { PageContainer } from '@ant-design/pro-components';
import { useIntl } from '@umijs/max';
import { useMemoizedFn } from 'ahooks';
import { ConfigProvider, Empty, message, Switch, Table } from 'antd';
import { Button, ConfigProvider, message, Table } from 'antd';
import { useState } from 'react';
import styled from 'styled-components';
import NoResult from '../_components/no-result';
import { createUser, deleteUser, queryUsersList, updateUser } from './apis';
import AddModal from './components/add-modal';
import { FormData, ListItem } from './config/types';
import useUsersColumns from './hooks/use-users-columns';
const StyledSwitch = styled(Switch)`
.ant-switch-handle::before {
inset-inline-end: 0 !important;
inset-inline-start: 0 !important;
}
`;
const Users: React.FC = () => {
const {
dataSource,
@@ -136,14 +130,24 @@ const Users: React.FC = () => {
const renderEmpty = (type?: string) => {
if (type !== 'Table') return;
if (
!dataSource.loading &&
dataSource.loadend &&
!dataSource.dataList.length
) {
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty>;
}
return <div></div>;
return (
<NoResult
loading={dataSource.loading}
loadend={dataSource.loadend}
dataSource={dataSource.dataList}
image={<IconFont type="icon-users" />}
filters={queryParams}
noFoundText={intl.formatMessage({
id: 'noresult.users.nofound'
})}
title={intl.formatMessage({ id: 'noresult.users.title' })}
subTitle={intl.formatMessage({ id: 'noresult.users.subTitle' })}
>
<Button type="primary" onClick={handleAddUser}>
{intl.formatMessage({ id: 'noresult.button.add' })}
</Button>
</NoResult>
);
};
const columns = useUsersColumns({