refactor: create cluster ux

This commit is contained in:
jialin
2026-01-06 17:21:32 +08:00
parent 66e8f83395
commit c821a2e463
10 changed files with 200 additions and 67 deletions
+73 -45
View File
@@ -1,13 +1,12 @@
import { clusterSessionAtom } from '@/atoms/clusters';
import { PageAction } from '@/config';
import { PageActionType } from '@/config/types';
import PageBreadcrumb from '@/pages/_components/page-breadcrumb';
import ColumnWrapper from '@/pages/_components/column-wrapper';
import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
import { useAtom } from 'jotai';
import _ from 'lodash';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import styled from 'styled-components';
import { PageContainerInner } from '../_components/page-box';
import {
createCluster,
queryClusterList,
@@ -38,10 +37,28 @@ const Container = styled.div`
`;
const Content = styled.div`
width: 600px;
width: 100%;
`;
const ClusterCreate = () => {
const StepsWrapper = styled.div`
width: 230px;
padding-inline: 24px;
padding-block: 16px;
// background-color: var(--ant-color-fill-quaternary);
border-right: 1px solid var(--ant-color-split);
.ant-steps.ant-steps-vertical {
height: 600px;
}
`;
const MainWrapper = styled.div`
display: flex;
height: 100%;
`;
const ClusterCreate: React.FC<{
onClose?: () => void;
}> = ({ onClose }) => {
const intl = useIntl();
const startStep = 0;
const stepList = useStepList();
@@ -297,10 +314,12 @@ const ClusterCreate = () => {
firstAddCluster: false,
firstAddWorker: false
});
navigate(`/cluster-management/clusters/list`);
// navigate(`/cluster-management/clusters/list`);
onClose?.();
return;
}
navigate(-1);
onClose?.();
// navigate(-1);
};
const breadcrumbItems = [
@@ -314,46 +333,55 @@ const ClusterCreate = () => {
];
return (
<PageContainerInner
footer={[
<FooterButtons
key="buttons"
onPrevious={onPrevious}
onNext={onNext}
handleCancel={handleCancel}
handleSubmit={handleSubmit}
loading={submitLoading}
showButtons={showButtons}
/>
]}
header={{
title: <PageBreadcrumb items={breadcrumbItems} />
}}
>
<ClusterSteps steps={steps} currentStep={currentStep}></ClusterSteps>
<div style={{ width: 800, margin: '0 auto' }}>
{currentStep === startStep && (
<ProviderCatalog
dataList={providerList}
height={70}
onSelect={handleSelectProvider}
clickable={true}
current={extraData.provider}
<MainWrapper>
<StepsWrapper>
<ClusterSteps steps={steps} currentStep={currentStep}></ClusterSteps>
</StepsWrapper>
<ColumnWrapper
styles={{
container: { paddingTop: 16, paddingBottom: 56 }
}}
footer={
<FooterButtons
key="buttons"
onPrevious={onPrevious}
onNext={onNext}
handleCancel={handleCancel}
handleSubmit={handleSubmit}
showButtons={showButtons}
loading={submitLoading}
/>
)}
<StepsContext.Provider
value={{
formValues: formValues,
systemConfig: systemConfig
}}
>
{renderModules()}
<Container>
<Content>{renderForms()}</Content>
</Container>
</StepsContext.Provider>
</div>
</PageContainerInner>
}
>
<div style={{ flex: 1 }}>
{currentStep === startStep && (
<ProviderCatalog
groupIcons={{
'clusters.create.provider.self': 'icon-server02',
'clusters.create.provider.cloud': 'icon-cloud'
}}
cols={2}
dataList={providerList}
height={70}
onSelect={handleSelectProvider}
clickable={true}
current={extraData.provider}
/>
)}
<StepsContext.Provider
value={{
formValues: formValues,
systemConfig: systemConfig
}}
>
{renderModules()}
<Container>
<Content>{renderForms()}</Content>
</Container>
</StepsContext.Provider>
</div>
</ColumnWrapper>
</MainWrapper>
);
};
@@ -0,0 +1,37 @@
import GSDrawer from '@/components/scroller-modal/gs-drawer';
import React from 'react';
import ClusterCreate from './cluster-create';
interface ClusterModalProps {
open: boolean;
onClose: () => void;
}
const ClusterModal: React.FC<ClusterModalProps> = ({ open, onClose }) => {
const handleCancel = () => {
onClose();
};
return (
<GSDrawer
title={'Create Cluster'}
open={open}
onClose={handleCancel}
destroyOnHidden={true}
closeIcon={false}
maskClosable={false}
keyboard={false}
width={900}
styles={{
body: {
paddingBlock: 0
}
}}
footer={false}
>
<ClusterCreate onClose={handleCancel}></ClusterCreate>
</GSDrawer>
);
};
export default ClusterModal;
+11 -1
View File
@@ -30,6 +30,7 @@ import {
updateCluster,
WORKER_POOLS_API
} from './apis';
import ClusterModal from './cluster-modal';
import AddCluster from './components/add-cluster';
import AddPool from './components/add-pool';
import {
@@ -47,6 +48,7 @@ import {
} from './config/types';
import useAddWorker from './hooks/use-add-worker';
import useClusterColumns from './hooks/use-cluster-columns';
import useCreateCluster from './hooks/use-create-cluster';
const Clusters: React.FC = () => {
const {
@@ -77,6 +79,10 @@ const Clusters: React.FC = () => {
const intl = useIntl();
const { handleAddWorker, checkDefaultCluster, AddWorkerModal, setStepList } =
useAddWorker({});
const { clusterModalStatus, openClusterModal, closeClusterModal } =
useCreateCluster({
refresh: handleSearch
});
const [openAddModal, setOpenAddModal] = useState<{
open: boolean;
@@ -124,7 +130,7 @@ const Clusters: React.FC = () => {
};
const handleClickDropdown = () => {
navigate(`/cluster-management/clusters/create?action=${PageAction.CREATE}`);
openClusterModal();
};
const handleModalOk = async (data: FormData) => {
@@ -396,6 +402,10 @@ const Clusters: React.FC = () => {
onOk={handleSubmitWorkerPool}
></AddPool>
<DeleteModal ref={modalRef}></DeleteModal>
<ClusterModal
open={clusterModalStatus.open}
onClose={closeClusterModal}
></ClusterModal>
{AddWorkerModal}
</>
);
@@ -7,8 +7,7 @@ const { Step } = Steps;
const Wrapper = styled.div`
width: min(1200px, 100%);
margin: 0 auto;
padding-block: 30px;
background-color: var(--ant-color-bg-container);
padding-block: 0px;
.ant-steps-item-description {
max-width: 300px !important;
color: var(--ant-color-text-description) !important;
@@ -24,7 +23,12 @@ const ClusterSteps: React.FC<{
return (
<Wrapper>
<Steps current={currentStep} size="small" onChange={onChange} progressDot>
<Steps
current={currentStep}
size="small"
onChange={onChange}
direction="vertical"
>
{steps.map((step) => (
<Step
key={step.title}
@@ -37,15 +37,19 @@ const FooterButtons: React.FC<FooterButtonsProps> = (props) => {
onNext();
};
return (
<Title style={{ height: 64 }}>
<div className="flex-center gap-16">
<Title style={{ padding: '16px 24px 32px' }}>
<div className="flex-center gap-20">
{showButtons.previous && (
<Button type="link" icon={<LeftOutlined />} onClick={onPrevious}>
<Button type="default" icon={<LeftOutlined />} onClick={onPrevious}>
{intl.formatMessage({ id: 'common.button.prev' })}
</Button>
)}
{showButtons.next && (
<Button type="link" onClick={handleOnNext}>
<Button
type="primary"
onClick={handleOnNext}
style={{ minWidth: 100 }}
>
{intl.formatMessage({ id: 'common.button.next' })}
<RightOutlined />
</Button>
@@ -62,7 +66,8 @@ const FooterButtons: React.FC<FooterButtonsProps> = (props) => {
style={{ minWidth: 88 }}
loading={loading}
>
{intl.formatMessage({ id: 'common.button.save' })}
{/* {intl.formatMessage({ id: 'common.button.save' })} */}
Create & Continue
</Button>
)}
</div>
@@ -1,3 +1,4 @@
import IconFont from '@/components/icon-font';
import Card from '@/components/templates/card';
import { useIntl } from '@umijs/max';
import React, { useMemo } from 'react';
@@ -10,7 +11,7 @@ const Wrapper = styled.div<{ $cols?: number }>`
grid-template-columns: repeat(${(props) => props.$cols || 3}, 1fr);
gap: 16px;
.template-card-wrapper {
padding: 10px;
padding: 10px 16px;
}
.template-card-inner {
justify-content: center;
@@ -21,16 +22,26 @@ const Container = styled.div`
display: flex;
margin: 0 auto;
flex-direction: column;
gap: 16px;
gap: 24px;
`;
const Title = styled.span`
display: flex;
gap: 8px;
align-items: center;
justify-content: space-between;
font-weight: 700;
font-size: 16px;
margin-block: 20px 16px;
font-size: 14px;
margin-bottom: 16px;
.icon {
color: var(--ant-color-text-secondary);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
background-color: var(--ant-color-fill-tertiary);
}
`;
const Header = styled.div`
@@ -38,6 +49,9 @@ const Header = styled.div`
flex-direction: column;
align-items: flex-start;
gap: 4px;
.title {
font-weight: 500;
}
.description {
font-size: 13px;
font-weight: 400;
@@ -47,6 +61,7 @@ const Header = styled.div`
interface ProviderCatalogProps {
onSelect?: (provider: string, item: any) => void;
groupIcons?: Record<string, string>;
cols?: number;
current?: ProviderType | string;
clickable?: boolean;
@@ -64,6 +79,7 @@ interface ProviderCatalogProps {
const ProviderCatalog: React.FC<ProviderCatalogProps> = ({
onSelect,
groupIcons,
cols = 3,
dataList,
clickable,
@@ -86,7 +102,7 @@ const ProviderCatalog: React.FC<ProviderCatalogProps> = ({
const renderTitle = (item: any) => {
return (
<Header>
<span>
<span className="title">
{item.locale ? intl.formatMessage({ id: item.label }) : item.label}
</span>
{item.description && (
@@ -103,7 +119,14 @@ const ProviderCatalog: React.FC<ProviderCatalogProps> = ({
{Object.entries(groupList).map(([groupName, items]) => (
<div key={groupName}>
{groupName !== 'default' && (
<Title>{intl.formatMessage({ id: groupName })}</Title>
<Title>
{groupIcons && groupIcons[groupName] && (
<span className="icon">
<IconFont type={groupIcons[groupName]} />
</span>
)}
<span>{intl.formatMessage({ id: groupName })}</span>
</Title>
)}
<Wrapper $cols={cols}>
{items?.map((action) => (
@@ -141,11 +141,11 @@ const SupportedHardware: React.FC<SupportedHardwareProps> = ({
<Box>
<ProviderCatalog
onSelect={onSelect}
height={50}
height={60}
current={current}
dataList={supportedHardPlatforms}
clickable={clickable}
cols={4}
cols={3}
/>
</Box>
);
@@ -0,0 +1,26 @@
import { useState } from 'react';
const useCreateCluster = (options: { refresh: () => void }) => {
const [clusterModalStatus, setClusterModalStatus] = useState<{
open: boolean;
}>({
open: false
});
const openClusterModal = () => {
setClusterModalStatus({ open: true });
};
const closeClusterModal = () => {
setClusterModalStatus({ open: false });
options.refresh();
};
return {
clusterModalStatus,
openClusterModal,
closeClusterModal
};
};
export default useCreateCluster;
@@ -10,8 +10,8 @@ const Title = styled.span`
display: flex;
align-items: center;
justify-content: space-between;
font-weight: 700;
font-size: 16px;
font-weight: 600;
font-size: 14px;
.text {
font-size: 20px;
}
@@ -30,8 +30,8 @@ const Title = styled.span`
display: flex;
align-items: center;
justify-content: space-between;
font-weight: 700;
font-size: 16px;
font-weight: 600;
font-size: 14px;
.text {
font-size: 20px;
}