fix: instance form ux

This commit is contained in:
jialin
2026-05-25 22:42:02 +08:00
committed by jialin
parent 5195b1e5fc
commit 921a2a0d81
14 changed files with 92 additions and 44 deletions
+2
View File
@@ -42,6 +42,8 @@ export default {
'Please make sure the <a href={link} target="_blank">prerequisites</a> for {label} are met before executing the following command.',
'clusters.create.addCommand.tips':
'On the Worker that needs to be added, run the following command to join it to the cluster.',
'clusters.create.addCommand.k8s.tips':
'On the Kubernetes cluster that needs to be registered, run the following command to create the Kubernetes resources and register the cluster.',
'clusters.create.register.tips':
'On the Kubernetes cluster that needs to be added, run the following command to join its nodes to the cluster.',
'cluster.create.checkEnv.tips':
+2
View File
@@ -42,6 +42,8 @@ export default {
'Please make sure the <a href={link} target="_blank">prerequisites</a> for {label} are met before executing the following command.',
'clusters.create.addCommand.tips':
'On the Worker that needs to be added, run the following command to join it to the cluster.',
'clusters.create.addCommand.k8s.tips':
'登録する Kubernetes クラスターで以下のコマンドを実行し、Kubernetes リソースを作成してクラスターを登録します。',
'cluster.create.checkEnv.tips':
'Use the following command to check if the environment is ready.',
'clusters.create.register.tips':
+2
View File
@@ -42,6 +42,8 @@ export default {
'Пожалуйста, убедитесь, что выполнены <a href={link} target="_blank">предварительные условия</a> для {label} перед выполнением следующей команды.',
'clusters.create.addCommand.tips':
'На воркере, который необходимо добавить, выполните следующую команду, чтобы присоединить его к кластеру.',
'clusters.create.addCommand.k8s.tips':
'На Kubernetes-кластере, который необходимо зарегистрировать, выполните следующую команду, чтобы создать ресурсы Kubernetes и зарегистрировать этот кластер.',
'cluster.create.checkEnv.tips':
'Используйте следующую команду для проверки готовности окружения',
'clusters.create.register.tips':
+2
View File
@@ -42,6 +42,8 @@ export default {
'Aşağıdaki komutu çalıştırmadan önce lütfen {label} için <a href={link} target="_blank">ön koşulların</a> karşılandığından emin olun.',
'clusters.create.addCommand.tips':
'Eklenmesi gereken İşçi Düğümde, kümeye katılması için aşağıdaki komutu çalıştırın.',
'clusters.create.addCommand.k8s.tips':
'Kaydedilmesi gereken Kubernetes kümesinde, Kubernetes kaynaklarını oluşturmak ve kümeyi kaydetmek için aşağıdaki komutu çalıştırın.',
'clusters.create.register.tips':
'Eklenmesi gereken Kubernetes kümesinde, düğümlerini kümeye katılması için aşağıdaki komutu çalıştırın.',
'cluster.create.checkEnv.tips':
+2
View File
@@ -41,6 +41,8 @@ export default {
'在执行以下命令之前,请确保已满足 {label} 的<a href={link} target="_blank">先决条件</a>。',
'clusters.create.addCommand.tips':
'在需要添加的节点上运行以下命令,将其加入到集群中。',
'clusters.create.addCommand.k8s.tips':
'在需要注册的 Kubernetes 集群中运行以下命令,创建 Kubernetes 资源,注册该集群。',
'clusters.create.register.tips':
'在需要添加的 Kubernetes 集群上运行以下命令,将其中的节点加入到集群中。',
'cluster.create.checkEnv.tips': '使用以下命令检查环境是否准备妥当。',
@@ -113,7 +113,7 @@ const NumberSelection: React.FC<NumberSelectionProps> = ({
aria-disabled={disabled}
tabIndex={disabled ? -1 : 0}
className={classNames(styles.numberItem, {
[styles.active]: num === value
[styles.active]: num === value && !!value
})}
onClick={() => handleSelect(num)}
onKeyDown={(e) => {
@@ -30,7 +30,7 @@ const K8sRunCommand: React.FC<AddWorkerStepProps> = ({ disabled }) => {
}}
>
{intl.formatMessage({
id: 'clusters.create.addCommand.tips'
id: 'clusters.create.addCommand.k8s.tips'
})}
</Typography.Paragraph>
<RegisterClusterInner
@@ -2,6 +2,7 @@ import { AutoTooltip, IconFont, StatusTag, ThemeTag } from '@gpustack/core-ui';
import { useIntl } from '@umijs/max';
import { Flex } from 'antd';
import styled from 'styled-components';
import { manufactureColorMap } from '../../templates/config';
import {
convertKiToGi,
InstanceTypePhaseLabelMap,
@@ -67,7 +68,10 @@ const InstanceTypeItem: React.FC<InstanceTypeItemProps> = ({
const name = item.metadata?.name;
const acceleratable = item.spec?.acceleratable;
const manufacturer = item.spec?.manufacturer?.toUpperCase();
const manufacturerKey = item.spec?.manufacturer;
const manufacturer = manufacturerKey?.toUpperCase();
const manufacturerColor =
(manufacturerKey && manufactureColorMap[manufacturerKey]) ?? 'purple';
const renderName = () => {
const product = item.spec?.product;
@@ -91,7 +95,7 @@ const InstanceTypeItem: React.FC<InstanceTypeItemProps> = ({
fontWeight: 400
}}
>
<ThemeTag color="purple">{manufacturer}</ThemeTag>
<ThemeTag color={manufacturerColor}>{manufacturer}</ThemeTag>
</span>
)}
</Flex>
@@ -6,6 +6,7 @@ export interface FormData {
name: string;
namespace: string;
};
enable_ssh?: boolean;
spec: {
type: string;
image: string;
+48 -23
View File
@@ -4,8 +4,8 @@ import {
CheckboxField,
CollapsePanel,
IconFont,
MultipleSelect,
ScrollSpyTabs,
Select,
useFinishFailed,
useScrollActiveChange,
useWrapperContext
@@ -25,6 +25,7 @@ import TemplateBasicForm, {
BasicResourceMax
} from '../../templates/forms/basic';
import { FormData, InstanceTypeItem, ListItem } from '../config/types';
import instanceStyles from '../styles/instances.module.less';
import Basic from './basic';
import InstanceTypeFormItem from './instance-type';
import StorageVolume from './storage-volume';
@@ -123,6 +124,22 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
}
}, [open, action]);
const handleSSHEnableChange = (e: any) => {
if (!e?.target?.checked) {
return;
}
const currentPorts = form.getFieldValue(['spec', 'ports']) || [];
const hasSSHPort = currentPorts.some(
(item: any) => item?.protocol === 'TCP' && item?.port === SSH_PORT
);
if (!hasSSHPort) {
form.setFieldValue(
['spec', 'ports'],
[...currentPorts, { protocol: 'TCP', port: SSH_PORT, name: 'SSH' }]
);
}
};
const segmentOptions = useMemo(
() => [
{
@@ -363,31 +380,39 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
<CheckboxField
disabled={disabled}
label={'启用 SSH 访问'}
onChange={handleSSHEnableChange}
></CheckboxField>
</Form.Item>
{sshEnabled && (
<Form.Item<FormData> name={['spec', 'sshPublicKey', 'name']}>
<Select
disabled={disabled}
mode="multiple"
maxTagCount={1}
label={'SSH 公钥'}
options={[
{
label: 'SSH-key-1',
value: 'gpustack-ssh-public-key'
},
{
label: 'SSH-key-2',
value: 'gpustack-ssh-public-key-2'
},
{
label: 'SSH-key-3',
value: 'gpustack-ssh-public-key-3'
}
]}
></Select>
</Form.Item>
<div className={instanceStyles.sshkeySelection}>
<Form.Item<FormData> name={['spec', 'sshPublicKey', 'name']}>
<MultipleSelect
disabled={disabled}
mode="multiple"
maxTagCount={1}
label={'SSH 公钥'}
styles={{
wrapper: {
width: '100%'
}
}}
options={[
{
label: 'SSH-key-1',
value: 'gpustack-ssh-public-key'
},
{
label: 'SSH-key-2',
value: 'gpustack-ssh-public-key-2'
},
{
label: 'SSH-key-3',
value: 'gpustack-ssh-public-key-3'
}
]}
></MultipleSelect>
</Form.Item>
</div>
)}
</Form>
</ScrollSpyTabs>
@@ -87,7 +87,8 @@ const StorageVolume = ({
};
return (
<FieldBlock>
<>
<div data-field="storage"></div>
<Flex
style={{
justifyContent: 'space-between',
@@ -149,7 +150,6 @@ const StorageVolume = ({
</Button>
)}
</Flex>
<div data-field="storage"></div>
{storageMode === StorageModeValueMap.Existing && (
<Form.Item
@@ -230,7 +230,7 @@ const StorageVolume = ({
onCancel={() => setOverlayOpen(false)}
onSubmit={handleCreateStorage}
/>
</FieldBlock>
</>
);
};
@@ -32,3 +32,11 @@
}
}
}
.sshkeySelection {
:global {
.ant-select-content .ant-tag {
font-size: 12px !important;
}
}
}
@@ -27,7 +27,7 @@ import {
import { useIntl } from '@umijs/max';
import { Button, Tag } from 'antd';
import styled from 'styled-components';
import { templateActions } from '../config';
import { manufactureColorMap, templateActions } from '../config';
import { ListItem } from '../config/types';
const imageLogoMap = {
@@ -40,19 +40,6 @@ const imageLogoMap = {
ubuntu: ubuntuLogo
} as const;
export const manufactureColorMap: Record<string, string> = {
nvidia: 'green',
amd: 'volcano',
ascend: 'orange',
hygon: 'magenta',
moorthreads: 'cyan',
iluvatar: 'purple',
metax: 'geekblue',
cambricon: 'gold',
thead: 'red',
cpu: 'blue'
};
const manufacturerLabelMap: Record<string, string> = Object.values(
GPUsConfigs
).reduce(
@@ -35,6 +35,19 @@ export const ImagePullPolicyOptions: {
export const DefaultImagePullPolicy: ImagePullPolicy = 'IfNotPresent';
export const manufactureColorMap: Record<string, string> = {
nvidia: 'green',
amd: 'volcano',
ascend: 'orange',
hygon: 'magenta',
moorthreads: 'cyan',
iluvatar: 'purple',
metax: 'geekblue',
cambricon: 'gold',
thead: 'red',
cpu: 'blue'
};
export const normalizeCommand = (value: string): string[] => {
if (!value) return [];
const tokens: string[] = [];