diff --git a/src/assets/styles/common.less b/src/assets/styles/common.less index 6c22912e..7bc5c764 100644 --- a/src/assets/styles/common.less +++ b/src/assets/styles/common.less @@ -253,6 +253,10 @@ font-weight: var(--font-weight-bold); } +.text-primary { + color: var(--ant-color-text); +} + .text-secondary { color: var(--ant-color-text-secondary); } diff --git a/src/components/echarts/config.ts b/src/components/echarts/config.ts index 050736fa..b5207520 100644 --- a/src/components/echarts/config.ts +++ b/src/components/echarts/config.ts @@ -126,7 +126,6 @@ export default function useChartConfig() { type: 'bar', barMaxWidth: 20, barMinWidth: 8, - // stack: 'total', barGap: '30%', barCategoryGap: '50%' }; @@ -154,7 +153,7 @@ export default function useChartConfig() { progress: { show: true, roundCap: false, - width: 10 + width: 12 }, pointer: { length: '80%', @@ -166,7 +165,7 @@ export default function useChartConfig() { axisLine: { roundCap: false, lineStyle: { - width: 10, + width: 12, color: [ [0.5, 'rgba(84, 204, 152, 80%)'], [0.8, 'rgba(250, 173, 20, 80%)'], @@ -175,8 +174,8 @@ export default function useChartConfig() { } }, axisTick: { - distance: -10, - length: 5, + distance: -11, + length: 6, splitNumber: 5, lineStyle: { width: 1.5, @@ -209,13 +208,13 @@ export default function useChartConfig() { rich: { value: { fontSize: 16, - fontWeight: '600', + fontWeight: 500, color: chartColorMap.titleColor }, unit: { fontSize: 14, color: chartColorMap.titleColor, - fontWeight: '600', + fontWeight: 500, padding: [0, 0, 0, 2] } } diff --git a/src/components/echarts/gauge.tsx b/src/components/echarts/gauge.tsx index a4dc5e9d..808a791c 100644 --- a/src/components/echarts/gauge.tsx +++ b/src/components/echarts/gauge.tsx @@ -24,8 +24,10 @@ const GaugeChart: React.FC> = ( } = useChartConfig(); const { value, height, width, labelFormatter, title, color, gaugeConfig } = props; + const titleText = typeof title === 'string' ? title : title?.text; + if (!value && value !== 0) { - return ; + return ; } const setDataOptions = () => { @@ -41,14 +43,15 @@ const GaugeChart: React.FC> = ( return { title: { ...titleConfig, - text: title, + text: titleText, textStyle: { fontSize: 12, color: chartColorMap.colorSecondary, fontWeight: 400 }, - top: '0', - left: 'center' + top: 10, + left: 'center', + ...(typeof title === 'object' ? title : {}) }, series: [ { @@ -68,6 +71,10 @@ const GaugeChart: React.FC> = ( }, detail: { ...combineGaugeConfig.detail, + borderColor: colorValue, + lineHeight: 20, + height: 18, + width: 50, formatter: labelFormatter || gaugeItemConfig.detail.formatter }, data: [{ value }] diff --git a/src/components/echarts/types.ts b/src/components/echarts/types.ts index 44f71cc3..c5a824b2 100644 --- a/src/components/echarts/types.ts +++ b/src/components/echarts/types.ts @@ -21,7 +21,7 @@ export interface ChartProps { tooltipValueFormatter?: (val: any) => string; height: string | number; width?: string | number; - title?: string; + title?: string | TitleComponentOption; titleOptions?: { [K in keyof TitleComponentOption]?: TitleComponentOption[K]; }; diff --git a/src/components/icon-font/index.tsx b/src/components/icon-font/index.tsx index a202c05f..858018cd 100644 --- a/src/components/icon-font/index.tsx +++ b/src/components/icon-font/index.tsx @@ -2,7 +2,7 @@ import { createFromIconfontCN } from '@ant-design/icons'; // import './iconfont/iconfont.js'; const IconFont = createFromIconfontCN({ - scriptUrl: '//at.alicdn.com/t/c/font_4613488_ab78o2vlum.js' + scriptUrl: '//at.alicdn.com/t/c/font_4613488_nb1thiz3t9.js' }); export default IconFont; diff --git a/src/components/page-tools/index.tsx b/src/components/page-tools/index.tsx index 89b91170..d249020b 100644 --- a/src/components/page-tools/index.tsx +++ b/src/components/page-tools/index.tsx @@ -167,7 +167,7 @@ export const FilterBar: React.FC = (props) => { return ( ; diff --git a/src/global.less b/src/global.less index 62ef3494..a9f606e4 100644 --- a/src/global.less +++ b/src/global.less @@ -118,6 +118,12 @@ body { width: 0; } + .ant-pro-page-container-affix { + .ant-affix { + top: 0 !important; + } + } + .ant-modal-confirm-title { display: none; } diff --git a/src/layouts/extraRender.tsx b/src/layouts/extraRender.tsx new file mode 100644 index 00000000..e68e0137 --- /dev/null +++ b/src/layouts/extraRender.tsx @@ -0,0 +1,298 @@ +import { GPUStackVersionAtom, UpdateCheckAtom } from '@/atoms/user'; +import DropDownActions from '@/components/drop-down-actions'; +import IconFont from '@/components/icon-font'; +import VersionInfo, { modalConfig } from '@/components/version-info'; +import externalLinks from '@/constants/external-links'; +import useBodyScroll from '@/hooks/use-body-scroll'; +import { logout } from '@/pages/login/apis'; +import { useModel } from '@@/plugin-model'; +import { + DiscordOutlined, + GithubOutlined, + HomeOutlined, + InfoCircleOutlined, + LogoutOutlined, + ReadOutlined, + SettingOutlined +} from '@ant-design/icons'; +import { history, useIntl, useNavigate } from '@umijs/max'; +import { Avatar, Divider, Modal } from 'antd'; +import { useAtom } from 'jotai'; +import { useMemo } from 'react'; +import styled from 'styled-components'; + +const IconWrapper = styled.span` + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + color: var(--ant-color-text-secondary); +`; + +const Wrapper = styled.div` + display: flex; + align-items: center; + gap: 24px; + height: 40px; +`; +const DropdownWrapper = styled.div` + min-width: 160px; + box-shadow: var(--ant-box-shadow-secondary); + background-color: var(--ant-color-bg-elevated); + border-radius: var(--ant-border-radius-lg); + padding: var(--ant-padding-xs); + .ant-dropdown-menu { + padding: 0; + box-shadow: none; + background-color: transparent; + border-radius: 0; + a { + color: var(--ant-color-text); + } + } +`; + +const CustomItem = styled.div` + display: flex; + align-items: center; + gap: 8px; + height: 32px; + justify-content: flex-start; + height: 32px; + padding: 0 var(--ant-padding-xs); + cursor: pointer; + &.user-info { + cursor: default; + justify-content: space-between; + .user-name { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + max-width: 100px; + } + } + &:not(.user-info):hover { + background-color: var(--ant-control-item-bg-hover); + } + background-color: var(--ant-color-bg-elevated); +`; + +export const ExtraContent = () => { + const { saveScrollHeight, restoreScrollHeight } = useBodyScroll(); + const [modal, contextHolder] = Modal.useModal(); + const [version] = useAtom(GPUStackVersionAtom); + const [updateCheck] = useAtom(UpdateCheckAtom); + const intl = useIntl(); + const initialInfo = useModel('@@initialState') || { + initialState: undefined, + loading: false, + setInitialState: null + }; + + const { initialState } = initialInfo; + + const navigate = useNavigate(); + + const loginPath = '/login'; + + const showUpgrade = useMemo(() => { + return ( + initialState?.currentUser?.is_admin && + updateCheck.latest_version && + updateCheck.latest_version !== version?.version && + updateCheck.latest_version?.indexOf('0.0.0') === -1 && + updateCheck.latest_version?.indexOf('rc') === -1 + ); + }, [ + updateCheck.latest_version, + version.version, + initialState?.currentUser?.is_admin + ]); + + const showVersion = () => { + saveScrollHeight(); + modal.info({ + ...modalConfig, + width: 460, + content: , + onCancel: restoreScrollHeight + }); + }; + + const handleLogout = async () => { + await logout(); + navigate(loginPath); + }; + + const helpList = [ + { + key: 'site', + icon: , + label: 'GPUStack', + url: externalLinks.site + }, + { + key: 'github', + icon: , + label: intl.formatMessage({ id: 'common.issue.report' }), + url: externalLinks.reportIssue + }, + { + key: 'faq', + icon: , + label: intl.formatMessage({ id: 'common.button.faq' }), + url: externalLinks.faq + }, + { + key: 'Discord', + icon: , + label: 'Discord', + url: externalLinks.discord + }, + { + key: 'docs', + icon: , + label: intl.formatMessage({ id: 'common.button.docs' }), + url: externalLinks.documentation + }, + { + key: 'version', + icon: , + label: intl.formatMessage({ id: 'common.button.version' }) + } + ]; + + const helpMenu = { + items: helpList.map((item) => ({ + key: item.key, + label: ( + + {item.icon} + {item.key === 'version' ? ( + <> + {item.label} + {showUpgrade && ( + + + {intl.formatMessage({ id: 'common.text.new' })} + + + )} + + ) : ( + + {item.label} + + )} + + ), + onClick() { + if (item.key === 'version') { + showVersion(); + } + } + })) + }; + + const userMenu = { + items: [ + { + key: 'apikeys', + label: ( + + + + {intl?.formatMessage?.({ id: 'menu.apikeys' })} + + + ), + onClick: () => { + history.push('/api-keys'); + } + }, + { + key: 'settings', + label: ( + + + + {intl?.formatMessage?.({ id: 'common.button.settings' })} + + + ), + onClick: () => { + history.push('/profile'); + } + } + ] + }; + + const userPopupRender = (originNode: React.ReactNode) => { + return ( + + + + + } + /> + + {initialState?.currentUser?.username} + + + + + {originNode} + + + + {intl?.formatMessage?.({ id: 'common.button.logout' })} + + + ); + }; + + const helpPopupRender = (originNode: React.ReactNode) => { + return {originNode}; + }; + + return ( + + {contextHolder} + + + + {showUpgrade && ( + + )} + + + + + } + /> + + + + ); +}; diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index 3292fed8..3ac8b85c 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -33,14 +33,26 @@ import 'driver.js/dist/driver.css'; import { useAtom } from 'jotai'; import 'overlayscrollbars/overlayscrollbars.css'; import { useEffect, useMemo, useState } from 'react'; +import { PageContainerInner } from '../pages/_components/page-box'; import Exception from './Exception'; import './Layout.css'; import { LogoIcon, SLogoIcon } from './Logo'; import ErrorBoundary from './error-boundary'; +import { ExtraContent } from './extraRender'; import { getRightRenderContent } from './rightRender'; import { patchRoutes } from './runtime'; import SiderMenu from './sider-menu'; +const NO_CONTAINER_PAGES = [ + 'chat', + 'rerank', + 'embedding', + 'speech', + 'image', + 'clusterDetail', + 'clusterCreate' +]; + const loginPath = '/login'; type InitialStateType = { @@ -205,8 +217,6 @@ export default (props: any) => { const role = initialState?.currentUser?.is_admin ? 'admin' : 'user'; const [route] = useAccessMarkedRoutes(mapRoutes(newRoutes, role)); - console.log('route============', route); - patchRoutes({ routes: route.children, initialState: initialInfo.initialState @@ -217,6 +227,12 @@ export default (props: any) => { [location.pathname] ); + const isNoContainerPage = useMemo(() => { + return NO_CONTAINER_PAGES.includes(matchedRoute?.name); + }, [matchedRoute]); + + console.log('matchedRoute=========', matchedRoute, route); + const allRouteKeys = useMemo(() => { const keys = new Set(); const childrenRoutes = route?.children || []; @@ -257,27 +273,6 @@ export default (props: any) => { } }, [initialize]); - useEffect(() => { - const checkAndInitialize = () => { - const menuWrap = window.document.querySelector( - '.ant-menu.ant-menu-root' - )?.parentElement; - if (menuWrap) { - try { - initializeMenu(menuWrap); - } catch (error) { - console.error('Failed to initialize menu:', error); - } - } else { - console.warn('Menu wrapper not found.'); - } - }; - - const timeout = setTimeout(checkAndInitialize, 500); - - return () => clearTimeout(timeout); - }, [initializeMenu, matchedRoute, location]); - const collapsed = useMemo(() => { return userSettings.collapsed || false; }, [userSettings.collapsed]); @@ -326,7 +321,6 @@ export default (props: any) => { }; const actionRender = (layoutProps) => { - console.log('actionRender', layoutProps); const dom = getRightRenderContent({ runtimeConfig, loading, @@ -334,8 +328,8 @@ export default (props: any) => { setInitialState, intl, isDarkTheme: userSettings.isDarkTheme, - siderWidth: layoutProps.siderWidth, - collapsed: layoutProps.collapsed, + siderWidth: layoutProps?.siderWidth, + collapsed: layoutProps?.collapsed, showUpgrade }); @@ -479,6 +473,12 @@ export default (props: any) => { return data; }, [userSettings.isDarkTheme, themeData]); + const outlet = runtimeConfig.childrenRender ? ( + runtimeConfig.childrenRender(, props) + ) : ( + + ); + return ( { > { onCollapse={onCollapse} onMenuHeaderClick={onMenuHeaderClick} menuHeaderRender={renderMenuHeader} + extra={[]} collapsed={userSettings.collapsed} onPageChange={onPageChange} formatMessage={formatMessage} @@ -521,10 +525,7 @@ export default (props: any) => { logo={collapsed ? SLogoIcon : LogoIcon} menuContentRender={menuContentRender} disableContentMargin - fixSiderbar - fixedHeader {...runtimeConfig} - actionsRender={actionRender} ErrorBoundary={ErrorBoundary} > { unAccessible={runtimeConfig?.unAccessible} noAccessible={runtimeConfig?.noAccessible} > - {runtimeConfig.childrenRender ? ( - runtimeConfig.childrenRender(, props) + {isNoContainerPage ? ( + outlet ) : ( - + {outlet} )} diff --git a/src/layouts/rightRender.tsx b/src/layouts/rightRender.tsx index ac011a9c..c9f8fd58 100644 --- a/src/layouts/rightRender.tsx +++ b/src/layouts/rightRender.tsx @@ -280,14 +280,8 @@ export const getRightRenderContent = (opts: { return ( <> - - + + ); }; diff --git a/src/pages/_components/page-box.tsx b/src/pages/_components/page-box.tsx new file mode 100644 index 00000000..fe44d4dd --- /dev/null +++ b/src/pages/_components/page-box.tsx @@ -0,0 +1,49 @@ +import { + PageContainer, + type PageContainerProps +} from '@ant-design/pro-components'; +import styled from 'styled-components'; + +const paddingInlinePageContainerContent = 32; + +const StyledPageContainer = styled(PageContainer)` + .ant-affix { + top: 0 !important; + height: auto; + } + .ant-page-header { + padding-block-end: 8px; + .ant-page-header-heading { + padding-block-start: 0; + } + } + .ant-pro-page-container-warp-page-header { + padding-inline: ${paddingInlinePageContainerContent}px; + } + .ant-page-header-heading-title { + font-size: 16px; + } +`; + +export const PageContainerInner: React.FC = ({ + children, + ...rest +}) => { + return ( + + {children} + + ); +}; + +const PageBox: React.FC<{ children: React.ReactNode }> = ({ children }) => { + return
{children}
; +}; + +export default PageBox; diff --git a/src/pages/_components/page-breadcrumb/index.tsx b/src/pages/_components/page-breadcrumb/index.tsx new file mode 100644 index 00000000..653f4b0b --- /dev/null +++ b/src/pages/_components/page-breadcrumb/index.tsx @@ -0,0 +1,27 @@ +import IconFont from '@/components/icon-font'; +import { Breadcrumb, type BreadcrumbProps } from 'antd'; +import React from 'react'; +import styled from 'styled-components'; + +const StyledBreadcrumb = styled(Breadcrumb)` + .ant-breadcrumb-link { + font-size: 16px; + font-weight: 500; + a { + background: unset; + } + } + .ant-breadcrumb-separator { + display: flex; + align-items: center; + } +`; + +const App: React.FC = ({ items }) => ( + } + items={items} + /> +); + +export default App; diff --git a/src/pages/_context/pageContainerContext.ts b/src/pages/_context/pageContainerContext.ts new file mode 100644 index 00000000..3f0d4deb --- /dev/null +++ b/src/pages/_context/pageContainerContext.ts @@ -0,0 +1,20 @@ +import { PageContainerProps } from '@ant-design/pro-components'; +import { createContext, useContext } from 'react'; + +interface PageContainerContextProps extends Partial { + [key: string]: any; +} + +export const PageContainerContext = createContext( + {} +); + +export function usePageContainerContext() { + const context = useContext(PageContainerContext); + if (!context) { + throw new Error( + 'usePageContainerContext must be used within a PageContainerProvider' + ); + } + return context; +} diff --git a/src/pages/api-keys/index.tsx b/src/pages/api-keys/index.tsx index ccccbec1..3e2e9a25 100644 --- a/src/pages/api-keys/index.tsx +++ b/src/pages/api-keys/index.tsx @@ -4,12 +4,12 @@ import { FilterBar } from '@/components/page-tools'; import { PageAction } from '@/config'; import type { 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, ConfigProvider, Table } from 'antd'; import { useState } from 'react'; import NoResult from '../_components/no-result'; +import PageBox from '../_components/page-box'; import { deleteApisKey, queryApisKeysList } from './apis'; import AddAPIKeyModal from './components/add-apikey-modal'; import { ListItem } from './config/types'; @@ -123,17 +123,7 @@ const APIKeys: React.FC = () => { return ( <> - + { }} > - + { }); return ( - + { } > - + ); }; diff --git a/src/pages/cluster-management/cluster-create.tsx b/src/pages/cluster-management/cluster-create.tsx index 7eb03169..02649004 100644 --- a/src/pages/cluster-management/cluster-create.tsx +++ b/src/pages/cluster-management/cluster-create.tsx @@ -1,10 +1,11 @@ import { PageAction } from '@/config'; import { PageActionType } from '@/config/types'; -import { PageContainer } from '@ant-design/pro-components'; +import PageBreadcrumb from '@/pages/_components/page-breadcrumb'; import { useIntl, useNavigate, useSearchParams } from '@umijs/max'; import _ from 'lodash'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import styled from 'styled-components'; +import { PageContainerInner } from '../_components/page-box'; import { createCluster, queryClusterToken, queryCredentialList } from './apis'; import ClusterSteps from './components/cluster-steps'; import FooterButtons from './components/footer-buttons'; @@ -25,7 +26,6 @@ const Container = styled.div` const Nav = styled.div` display: flex; align-items: center; - height: 72px; font-weight: 400; font-size: 20px; color: var(--ant-color-text-tertiary); @@ -280,9 +280,18 @@ const ClusterCreate = () => { navigate(-1); }; + const breadcrumbItems = [ + { + title: {intl.formatMessage({ id: 'clusters.title' })}, + onClick: () => navigate(-1) + }, + { + title: intl.formatMessage({ id: 'common.button.create' }) + } + ]; + return ( - { /> ]} header={{ - title: false, - style: { - paddingInline: 'var(--layout-content-header-inlinepadding)' - }, - breadcrumb: {} + title: }} - pageHeaderRender={() => ( - - - - - )} > + {currentStep === startStep && ( { {renderForms()} - + ); }; diff --git a/src/pages/cluster-management/cluster-detail.tsx b/src/pages/cluster-management/cluster-detail.tsx index b1d3ce36..5874c0ea 100644 --- a/src/pages/cluster-management/cluster-detail.tsx +++ b/src/pages/cluster-management/cluster-detail.tsx @@ -1,30 +1,33 @@ -import { PageContainer } from '@ant-design/pro-components'; -import { useSearchParams } from '@umijs/max'; +import { useIntl, useNavigate, useSearchParams } from '@umijs/max'; +import { PageContainerInner } from '../_components/page-box'; +import PageBreadcrumb from '../_components/page-breadcrumb'; import ClusterMetrics from './components/cluster-metrics'; const ClusterDetailModal = () => { + const navigate = useNavigate(); + const intl = useIntl(); const [searchParams] = useSearchParams(); const provider = searchParams.get('provider'); const clusterName = searchParams.get('name'); + + const breadcrumbItems = [ + { + title: {intl.formatMessage({ id: 'clusters.title' })}, + onClick: () => navigate(-1) + }, + { + title: clusterName + } + ]; + return ( - - {clusterName} - - ), - style: { - paddingInline: 'var(--layout-content-header-inlinepadding)' - }, - breadcrumb: {} + title: }} - extra={[]} > - {/* {provider === ProviderValueMap.DigitalOcean && } */} - + ); }; diff --git a/src/pages/cluster-management/clusters.tsx b/src/pages/cluster-management/clusters.tsx index ca0ab79e..b9f1b767 100644 --- a/src/pages/cluster-management/clusters.tsx +++ b/src/pages/cluster-management/clusters.tsx @@ -10,13 +10,13 @@ import useExpandedRowKeys from '@/hooks/use-expanded-row-keys'; import useTableFetch from '@/hooks/use-table-fetch'; import useWatchList from '@/hooks/use-watch-list'; 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 { Button, message } from 'antd'; import { useAtom } from 'jotai'; import { useEffect, useState } from 'react'; import NoResult from '../_components/no-result'; +import PageBox from '../_components/page-box'; import { CLUSTERS_API, createWorkerPool, @@ -327,19 +327,7 @@ const Clusters: React.FC = () => { return ( <> - + { }} > - + { { { { { return ( <> - + { }} > - + { { { { { @@ -8,19 +8,11 @@ const Dashboard: React.FC = () => { return ( <> - + - + ); }; diff --git a/src/pages/llmodels/catalog.tsx b/src/pages/llmodels/catalog.tsx index c44b2211..e60cef1b 100644 --- a/src/pages/llmodels/catalog.tsx +++ b/src/pages/llmodels/catalog.tsx @@ -7,7 +7,6 @@ import useBodyScroll from '@/hooks/use-body-scroll'; import { ScrollerContext } from '@/pages/_components/infinite-scroller/use-scroller-context'; import { IS_FIRST_LOGIN, writeState } from '@/utils/localstore/index'; import { SearchOutlined, SyncOutlined } from '@ant-design/icons'; -import { PageContainer } from '@ant-design/pro-components'; import { useIntl, useNavigate } from '@umijs/max'; import { useMemoizedFn } from 'ahooks'; import { Button, Input, Space, message } from 'antd'; @@ -16,6 +15,7 @@ import _ from 'lodash'; import React, { useCallback, useEffect, useState } from 'react'; import styled from 'styled-components'; import NoResult from '../_components/no-result'; +import PageBox from '../_components/page-box'; import { createModel, queryCatalogList } from './apis'; import CatalogList from './components/catalog-list'; import DelopyBuiltInModal from './components/deploy-builtin-modal'; @@ -219,19 +219,10 @@ const Catalog: React.FC = () => { }, [fetchData]); return ( - + { onCancel={handleDeployModalCancel} onOk={handleCreateModel} > - + ); }; diff --git a/src/pages/llmodels/components/table-list.tsx b/src/pages/llmodels/components/table-list.tsx index 4a0ba1ca..2e2aa51a 100644 --- a/src/pages/llmodels/components/table-list.tsx +++ b/src/pages/llmodels/components/table-list.tsx @@ -14,10 +14,10 @@ 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 PageBox from '@/pages/_components/page-box'; import { ListItem as WorkerListItem } from '@/pages/resources/config/types'; import { handleBatchRequest } from '@/utils'; import { DownOutlined, SearchOutlined, SyncOutlined } from '@ant-design/icons'; -import { PageContainer } from '@ant-design/pro-components'; import { useIntl, useNavigate } from '@umijs/max'; import { useMemoizedFn } from 'ahooks'; import { Button, Input, Space, message } from 'antd'; @@ -596,20 +596,10 @@ const Models: React.FC = ({ return ( <> - + = ({ onChange: handlePageChange }} > - + { }, [dataSource.dataList, status]); return ( - { subTitle={intl.formatMessage({ id: 'noresult.mymodels.subTitle' })} > - + ); }; diff --git a/src/pages/login/apis/index.ts b/src/pages/login/apis/index.ts index a1cb34c8..5ed5bd6b 100644 --- a/src/pages/login/apis/index.ts +++ b/src/pages/login/apis/index.ts @@ -23,7 +23,7 @@ export const login = async ( }); }; -export const logout = async (userInfo: any) => { +export const logout = async (userInfo?: any) => { await request(`${AUTH_API}/logout`, { method: 'POST' }); diff --git a/src/pages/playground/components/ground-stt.tsx b/src/pages/playground/components/ground-stt.tsx index e4318fdf..c4b96f96 100644 --- a/src/pages/playground/components/ground-stt.tsx +++ b/src/pages/playground/components/ground-stt.tsx @@ -5,6 +5,7 @@ import AudioPlayer from '@/components/audio-player'; import CopyButton from '@/components/copy-button'; import IconFont from '@/components/icon-font'; import UploadAudio from '@/components/upload-audio'; +import { HEADER_HEIGHT } from '@/config/constants'; import routeCachekey from '@/config/route-cachekey'; import useOverlayScroller from '@/hooks/use-overlay-scroller'; import { useCancelToken } from '@/hooks/use-request-token'; @@ -13,7 +14,6 @@ import { SendOutlined } from '@ant-design/icons'; import { useIntl, useSearchParams } from '@umijs/max'; import { Button, Spin, Tooltip } from 'antd'; import classNames from 'classnames'; -import 'overlayscrollbars/overlayscrollbars.css'; import React, { forwardRef, useCallback, @@ -289,7 +289,7 @@ const GroundSTT: React.FC = forwardRef((props, ref) => {
diff --git a/src/pages/playground/components/multiple-chat/index.tsx b/src/pages/playground/components/multiple-chat/index.tsx index b01ea3ba..5cac9487 100644 --- a/src/pages/playground/components/multiple-chat/index.tsx +++ b/src/pages/playground/components/multiple-chat/index.tsx @@ -1,6 +1,6 @@ +import { HEADER_HEIGHT } from '@/config/constants'; import useOverlayScroller from '@/hooks/use-overlay-scroller'; import _ from 'lodash'; -import 'overlayscrollbars/overlayscrollbars.css'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import CompareContext from '../../config/compare-context'; import { @@ -39,7 +39,7 @@ const MultiCompare: React.FC = ({ modelList, loaded }) => { const modelsCounterMap = useRef>({}); const modelRefs = useRef({}); const chatListScrollRef = useRef(null); - const boxHeight = 'calc(100vh - 72px)'; + const boxHeight = `calc(100vh - ${HEADER_HEIGHT}px)`; const [actions, setActions] = useState([ 'upload', 'delete', diff --git a/src/pages/playground/components/view-code-buttons.tsx b/src/pages/playground/components/view-code-buttons.tsx new file mode 100644 index 00000000..03c31cb8 --- /dev/null +++ b/src/pages/playground/components/view-code-buttons.tsx @@ -0,0 +1,40 @@ +import { useIntl } from '@umijs/max'; +import { Button, Space } from 'antd'; +import React from 'react'; +import IconFont from '../../../components/icon-font'; + +const ViewCodeButtons: React.FC<{ + handleViewCode: () => void; + handleToggleCollapse: () => void; + activeKey: string; +}> = ({ handleViewCode, handleToggleCollapse, activeKey }) => { + const intl = useIntl(); + + if (activeKey === 'compare') { + return null; + } + + return ( + + + + + ); +}; + +export default ViewCodeButtons; diff --git a/src/pages/playground/embedding.tsx b/src/pages/playground/embedding.tsx index e9081556..dc87a7c5 100644 --- a/src/pages/playground/embedding.tsx +++ b/src/pages/playground/embedding.tsx @@ -1,15 +1,17 @@ -import IconFont from '@/components/icon-font'; import HotKeys from '@/config/hotkeys'; +import { ExtraContent } from '@/layouts/extraRender'; import { modelCategoriesMap } from '@/pages/llmodels/config'; -import { PageContainer } from '@ant-design/pro-components'; import { useIntl } from '@umijs/max'; -import { Button, Space } from 'antd'; +import { useMemoizedFn } from 'ahooks'; +import { Divider } from 'antd'; import classNames from 'classnames'; import _ from 'lodash'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; +import { PageContainerInner } from '../_components/page-box'; import { queryModelsList } from './apis'; import GroundEmbedding from './components/ground-embedding'; +import ViewCodeButtons from './components/view-code-buttons'; import useCollapseLayout from './hooks/use-collapse-layout'; import './style/play-ground.less'; @@ -27,12 +29,12 @@ const PlaygroundEmbedding: React.FC = () => { triggeredRef: groundLeftRef.current }); - const handleViewCode = useCallback(() => { + const handleViewCode = useMemoizedFn(() => { groundLeftRef.current?.viewCode?.(); - }, [groundLeftRef.current]); - const handleToggleCollapse = useCallback(() => { + }); + const handleToggleCollapse = useMemoizedFn(() => { groundLeftRef.current?.setCollapse?.(); - }, [groundLeftRef.current]); + }); useEffect(() => { const getModelListByEmbedding = async () => { @@ -66,30 +68,6 @@ const PlaygroundEmbedding: React.FC = () => { fetchData(); }, []); - const renderExtra = () => { - return ( - - - - - ); - }; - useHotkeys( HotKeys.RIGHT.join(','), () => { @@ -101,17 +79,22 @@ const PlaygroundEmbedding: React.FC = () => { ); return ( - , + , + + ]} >
@@ -122,7 +105,7 @@ const PlaygroundEmbedding: React.FC = () => { >
-
+ ); }; diff --git a/src/pages/playground/images.tsx b/src/pages/playground/images.tsx index 6c1ed1cb..89aa5c57 100644 --- a/src/pages/playground/images.tsx +++ b/src/pages/playground/images.tsx @@ -1,19 +1,21 @@ -import IconFont from '@/components/icon-font'; import breakpoints from '@/config/breakpoints'; import HotKeys from '@/config/hotkeys'; import useWindowResize from '@/hooks/use-window-resize'; +import { ExtraContent } from '@/layouts/extraRender'; import { modelCategoriesMap } from '@/pages/llmodels/config'; import { DiffOutlined, HighlightOutlined } from '@ant-design/icons'; -import { PageContainer } from '@ant-design/pro-components'; import { useIntl } from '@umijs/max'; -import { Button, Segmented, Space, Tabs, TabsProps } from 'antd'; +import { useMemoizedFn } from 'ahooks'; +import { Divider, Segmented, Tabs, TabsProps } from 'antd'; import classNames from 'classnames'; import _ from 'lodash'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; +import { PageContainerInner } from '../_components/page-box'; import { queryModelsList } from './apis'; import GroundImages from './components/ground-images'; import ImageEdit from './components/image-edit'; +import ViewCodeButtons from './components/view-code-buttons'; import './style/play-ground.less'; const TabsValueMap = { @@ -44,21 +46,21 @@ const TextToImages: React.FC = () => { ]; }, [intl]); - const handleViewCode = useCallback(() => { + const handleViewCode = useMemoizedFn(() => { if (activeKey === TabsValueMap.Tab1) { groundTabRef1.current?.viewCode?.(); } else if (activeKey === TabsValueMap.Tab2) { groundTabRef2.current?.viewCode?.(); } - }, [activeKey]); + }); - const handleToggleCollapse = useCallback(() => { + const handleToggleCollapse = useMemoizedFn(() => { if (activeKey === TabsValueMap.Tab1) { groundTabRef1.current?.setCollapse?.(); return; } groundTabRef2.current?.setCollapse?.(); - }, [activeKey]); + }); const items: TabsProps['items'] = useMemo(() => { return [ @@ -124,30 +126,6 @@ const TextToImages: React.FC = () => { fetchData(); }, []); - const renderExtra = useMemo(() => { - return ( - - - - - ); - }, [intl, handleViewCode, handleToggleCollapse]); - const header = useMemo(() => { return { title: ( @@ -164,11 +142,7 @@ const TextToImages: React.FC = () => { > }
- ), - style: { - paddingInline: 'var(--layout-content-header-inlinepadding)' - }, - breadcrumb: {} + ) }; }, [optionsList]); @@ -183,10 +157,22 @@ const TextToImages: React.FC = () => { ); return ( - , + , + + ]} className={classNames('playground-container chat')} >
@@ -194,7 +180,7 @@ const TextToImages: React.FC = () => {
- + ); }; diff --git a/src/pages/playground/index.tsx b/src/pages/playground/index.tsx index 92c7a661..d6c21f40 100644 --- a/src/pages/playground/index.tsx +++ b/src/pages/playground/index.tsx @@ -1,21 +1,32 @@ -import IconFont from '@/components/icon-font'; import breakpoints from '@/config/breakpoints'; import HotKeys from '@/config/hotkeys'; import useWindowResize from '@/hooks/use-window-resize'; +import { ExtraContent } from '@/layouts/extraRender'; import { modelCategoriesMap } from '@/pages/llmodels/config'; import { MessageOutlined, OneToOneOutlined } from '@ant-design/icons'; -import { PageContainer } from '@ant-design/pro-components'; import { useIntl } from '@umijs/max'; -import { Button, Segmented, Space, Tabs, TabsProps } from 'antd'; +import { useMemoizedFn } from 'ahooks'; +import { Divider, Segmented, Tabs, TabsProps } from 'antd'; import classNames from 'classnames'; import _ from 'lodash'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; +import styled from 'styled-components'; +import { PageContainerInner } from '../_components/page-box'; import { queryModelsList } from './apis'; import GroundLeft from './components/ground-left'; import MultipleChat from './components/multiple-chat'; +import ViewCodeButtons from './components/view-code-buttons'; import './style/play-ground.less'; +const Header = styled.div` + display: flex; + align-items: center; + justify-content: space-between; + margin-top: 16px; + padding-inline: 32px; +`; + const Playground: React.FC = () => { const intl = useIntl(); const { size } = useWindowResize(); @@ -40,27 +51,28 @@ const Playground: React.FC = () => { ]; }, [intl]); - const handleViewCode = useCallback(() => { + const handleViewCode = useMemoizedFn(() => { if (activeKey === 'reranker') { groundRerankerRef.current?.viewCode?.(); } else if (activeKey === 'chat') { groundLeftRef.current?.viewCode?.(); } - }, [activeKey]); + }); - const handleToggleCollapse = useCallback(() => { + const handleToggleCollapse = useMemoizedFn(() => { if (activeKey === 'reranker') { groundRerankerRef.current?.setCollapse?.(); return; } groundLeftRef.current?.setCollapse?.(); - }, [activeKey]); + }); const items: TabsProps['items'] = useMemo(() => { return [ { key: 'chat', label: 'Chat', + icon: , children: ( ) @@ -68,6 +80,7 @@ const Playground: React.FC = () => { { key: 'compare', label: 'Compare', + icon: , children: } ]; @@ -105,33 +118,6 @@ const Playground: React.FC = () => { fetchData(); }, []); - const renderExtra = useMemo(() => { - if (activeKey === 'compare') { - return false; - } - return ( - - - - - ); - }, [activeKey, intl, handleViewCode, handleToggleCollapse]); - const header = useMemo(() => { return { title: ( @@ -148,11 +134,7 @@ const Playground: React.FC = () => { > } - ), - style: { - paddingInline: 'var(--layout-content-header-inlinepadding)' - }, - breadcrumb: {} + ) }; }, [optionsList]); @@ -169,21 +151,33 @@ const Playground: React.FC = () => { ); return ( - , + , + + ]} >
-
+ ); }; diff --git a/src/pages/playground/rerank.tsx b/src/pages/playground/rerank.tsx index b37ca7a8..dd33e25a 100644 --- a/src/pages/playground/rerank.tsx +++ b/src/pages/playground/rerank.tsx @@ -1,21 +1,22 @@ -import IconFont from '@/components/icon-font'; import HotKeys from '@/config/hotkeys'; +import { ExtraContent } from '@/layouts/extraRender'; import { modelCategoriesMap } from '@/pages/llmodels/config'; -import { PageContainer } from '@ant-design/pro-components'; import { useIntl } from '@umijs/max'; -import { Button, Space } from 'antd'; +import useMemoizedFn from 'ahooks/lib/useMemoizedFn'; +import { Divider } from 'antd'; import classNames from 'classnames'; import _ from 'lodash'; -import { useCallback, useEffect, useRef, useState } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; +import { PageContainerInner } from '../_components/page-box'; import { queryModelsList } from './apis'; import GroundReranker from './components/ground-reranker'; +import ViewCodeButtons from './components/view-code-buttons'; import useCollapseLayout from './hooks/use-collapse-layout'; import './style/play-ground.less'; const PlaygroundRerank: React.FC = () => { const intl = useIntl(); - const groundLeftRef = useRef(null); const groundRerankerRef = useRef(null); const [rerankerModelList, setRerankerModelList] = useState< Global.BaseOption[] @@ -29,13 +30,13 @@ const PlaygroundRerank: React.FC = () => { triggeredRef: groundRerankerRef.current }); - const handleViewCode = useCallback(() => { + const handleViewCode = useMemoizedFn(() => { groundRerankerRef.current?.viewCode?.(); - }, [groundRerankerRef]); + }); - const handleToggleCollapse = useCallback(() => { + const handleToggleCollapse = useMemoizedFn(() => { groundRerankerRef.current?.setCollapse?.(); - }, [groundRerankerRef]); + }); useEffect(() => { const getModelListByReranker = async () => { @@ -71,30 +72,6 @@ const PlaygroundRerank: React.FC = () => { fetchData(); }, []); - const renderExtra = () => { - return ( - - - - - ); - }; - useHotkeys( HotKeys.RIGHT.join(','), () => { @@ -106,17 +83,22 @@ const PlaygroundRerank: React.FC = () => { ); return ( - , + , + + ]} >
@@ -127,7 +109,7 @@ const PlaygroundRerank: React.FC = () => { >
-
+ ); }; diff --git a/src/pages/playground/speech.tsx b/src/pages/playground/speech.tsx index e45365c6..416cb85a 100644 --- a/src/pages/playground/speech.tsx +++ b/src/pages/playground/speech.tsx @@ -2,18 +2,21 @@ import IconFont from '@/components/icon-font'; import breakpoints from '@/config/breakpoints'; import HotKeys from '@/config/hotkeys'; import useWindowResize from '@/hooks/use-window-resize'; +import { ExtraContent } from '@/layouts/extraRender'; import { modelCategoriesMap } from '@/pages/llmodels/config'; import { AudioOutlined } from '@ant-design/icons'; -import { PageContainer } from '@ant-design/pro-components'; import { useIntl, useSearchParams } from '@umijs/max'; -import { Button, Segmented, Space, Tabs, TabsProps } from 'antd'; +import useMemoizedFn from 'ahooks/lib/useMemoizedFn'; +import { Divider, Segmented, Tabs, TabsProps } from 'antd'; import classNames from 'classnames'; import _ from 'lodash'; -import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; +import { PageContainerInner } from '../_components/page-box'; import { queryModelsList } from './apis'; import GroundSTT from './components/ground-stt'; import GroundTTS from './components/ground-tts'; +import ViewCodeButtons from './components/view-code-buttons'; import './style/play-ground.less'; const TabsValueMap = { @@ -53,21 +56,21 @@ const Playground: React.FC = () => { ]; }, [intl]); - const handleViewCode = useCallback(() => { + const handleViewCode = useMemoizedFn(() => { if (activeKey === TabsValueMap.Tab1) { groundTabRef1.current?.viewCode?.(); } else if (activeKey === TabsValueMap.Tab2) { groundTabRef2.current?.viewCode?.(); } - }, [activeKey]); + }); - const handleToggleCollapse = useCallback(() => { + const handleToggleCollapse = useMemoizedFn(() => { if (activeKey === TabsValueMap.Tab1) { groundTabRef1.current?.setCollapse?.(); return; } groundTabRef2.current?.setCollapse?.(); - }, [activeKey]); + }); const items: TabsProps['items'] = useMemo(() => { return [ @@ -160,30 +163,6 @@ const Playground: React.FC = () => { fetchData(); }, []); - const renderExtra = useMemo(() => { - return ( - - - - - ); - }, [handleToggleCollapse, handleViewCode, intl]); - const header = useMemo(() => { return { title: ( @@ -201,11 +180,7 @@ const Playground: React.FC = () => { > } - ), - style: { - paddingInline: 'var(--layout-content-header-inlinepadding)' - }, - breadcrumb: {} + ) }; }, [activeKey, optionsList]); @@ -220,10 +195,22 @@ const Playground: React.FC = () => { ); return ( - , + , + + ]} className={classNames('playground-container', { compare: activeKey === 'compare', chat: activeKey !== 'compare' @@ -234,7 +221,7 @@ const Playground: React.FC = () => { - + ); }; diff --git a/src/pages/playground/style/ground-left.less b/src/pages/playground/style/ground-left.less index e4011c69..791604e0 100644 --- a/src/pages/playground/style/ground-left.less +++ b/src/pages/playground/style/ground-left.less @@ -1,3 +1,5 @@ +@HEADER_HEIGHT: 56px; + .ground-left-wrapper { display: flex; @@ -7,7 +9,7 @@ width: 390px; border-left: 1px solid var(--ant-color-split); transition: width 0.3s ease; - height: calc(100vh - 72px); + height: calc(100vh - @HEADER_HEIGHT); padding-top: 16px; overflow-y: auto; @@ -40,7 +42,7 @@ justify-content: space-between; flex-direction: column; position: relative; - height: calc(100vh - 72px); + height: calc(100vh - @HEADER_HEIGHT); width: calc(100% - 390px); .message-list-wrap { diff --git a/src/pages/playground/style/play-ground.less b/src/pages/playground/style/play-ground.less index 7d0d7721..0ef8ab5e 100644 --- a/src/pages/playground/style/play-ground.less +++ b/src/pages/playground/style/play-ground.less @@ -1,3 +1,5 @@ +@HEADER_HEIGHT: 56px; + .play-ground { display: flex; align-items: flex-start; @@ -37,7 +39,7 @@ .params { overflow: hidden; - height: calc(100vh - 72px); + height: calc(100vh - @HEADER_HEIGHT); .collapse { width: 0; @@ -81,20 +83,19 @@ .ant-page-header { position: relative; - height: 72px; } &.chat { .ant-page-header { - &::after { - position: absolute; - content: ''; - left: 32px; - right: 32px; - bottom: 0; - height: 1px; - border-bottom: 1px solid var(--ant-color-split); - } + // &::after { + // position: absolute; + // content: ''; + // left: 32px; + // right: 32px; + // bottom: 0; + // height: 1px; + // border-bottom: 1px solid var(--ant-color-split); + // } } } diff --git a/src/pages/profile/index.tsx b/src/pages/profile/index.tsx index 95bccf08..8ece3d0a 100644 --- a/src/pages/profile/index.tsx +++ b/src/pages/profile/index.tsx @@ -1,9 +1,9 @@ import useTabActive from '@/hooks/use-tab-active'; -import { PageContainer } from '@ant-design/pro-components'; import { useIntl, useModel } from '@umijs/max'; -import { TabsProps } from 'antd'; +import { Tabs, TabsProps } from 'antd'; import React, { useCallback, useMemo, useState } from 'react'; import styled from 'styled-components'; +import PageBox from '../_components/page-box'; import Appearance from './components/appearance'; import ModifyPasswordn from './components/modify-password'; @@ -53,27 +53,14 @@ const Profile: React.FC = () => { }, []); return ( - - - + + + ); }; diff --git a/src/pages/resources/components/gpus.tsx b/src/pages/resources/components/gpus.tsx index 0ffbabf9..55148da6 100644 --- a/src/pages/resources/components/gpus.tsx +++ b/src/pages/resources/components/gpus.tsx @@ -2,8 +2,8 @@ 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 PageBox from '@/pages/_components/page-box'; import { queryClusterList } from '@/pages/cluster-management/apis'; -import { PageContainer } from '@ant-design/pro-components'; import { useIntl } from '@umijs/max'; import { ConfigProvider, Table } from 'antd'; import React, { useEffect, useState } from 'react'; @@ -76,17 +76,7 @@ const GPUList: React.FC = () => { return ( <> - + { }} > - + ); }; diff --git a/src/pages/resources/components/model-files.tsx b/src/pages/resources/components/model-files.tsx index aa1ad337..ab25a731 100644 --- a/src/pages/resources/components/model-files.tsx +++ b/src/pages/resources/components/model-files.tsx @@ -7,6 +7,7 @@ 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 PageBox from '@/pages/_components/page-box'; import { createModel } from '@/pages/llmodels/apis'; import DeployModal from '@/pages/llmodels/components/deploy-modal'; import { modelSourceMap } from '@/pages/llmodels/config'; @@ -20,7 +21,6 @@ import DownloadModal from '@/pages/llmodels/download'; import useCheckBackend from '@/pages/llmodels/hooks/use-check-backend'; import { useGenerateWorkerOptions } from '@/pages/llmodels/hooks/use-form-initial-values'; 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, Table, message } from 'antd'; @@ -290,17 +290,7 @@ const ModelFiles = () => { return ( <> - + { isGGUF={openDeployModal.isGGUF} clusterList={clusterList} > - + ); }; diff --git a/src/pages/resources/components/workers.tsx b/src/pages/resources/components/workers.tsx index 15bbe11e..702073b5 100644 --- a/src/pages/resources/components/workers.tsx +++ b/src/pages/resources/components/workers.tsx @@ -2,8 +2,8 @@ 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 PageBox from '@/pages/_components/page-box'; import { queryClusterList } from '@/pages/cluster-management/apis'; -import { PageContainer } from '@ant-design/pro-components'; import { useIntl, useNavigate } from '@umijs/max'; import { useMemoizedFn } from 'ahooks'; import { Button, ConfigProvider, Table, message } from 'antd'; @@ -227,7 +227,7 @@ const Workers: React.FC = () => { return ( <> - { onClose={handleTerminalClose} /> ]} - header={{ - title: intl.formatMessage({ id: 'resources.nodes' }), - style: { - paddingInline: 'var(--layout-content-header-inlinepadding)' - }, - breadcrumb: {} - }} - extra={[]} > { setWorkerDetailStatus({ currentData: null, open: false }) } /> - + ); }; diff --git a/src/pages/users/index.tsx b/src/pages/users/index.tsx index 54015128..a1c05075 100644 --- a/src/pages/users/index.tsx +++ b/src/pages/users/index.tsx @@ -4,12 +4,12 @@ import { FilterBar } from '@/components/page-tools'; import { PageAction } from '@/config'; import type { PageActionType } from '@/config/types'; import useTableFetch from '@/hooks/use-table-fetch'; -import { PageContainer } from '@ant-design/pro-components'; import { useIntl, useModel } from '@umijs/max'; import { useMemoizedFn } from 'ahooks'; import { Button, ConfigProvider, message, Table } from 'antd'; import { useMemo, useState } from 'react'; import NoResult from '../_components/no-result'; +import PageBox from '../_components/page-box'; import { createUser, deleteUser, queryUsersList, updateUser } from './apis'; import AddModal from './components/add-modal'; import { FormData, ListItem } from './config/types'; @@ -165,17 +165,7 @@ const Users: React.FC = () => { return ( <> - + { }} > - +