From 01fe2e66fb4c4fbc2c4d20a173e95e860f1c09e6 Mon Sep 17 00:00:00 2001 From: jialin Date: Mon, 11 May 2026 17:11:21 +0800 Subject: [PATCH] fix: style issues --- .gitignore | 1 + src/atoms/user.ts | 2 +- src/layouts/index.tsx | 2 +- src/locales/en-US/common.ts | 3 +- src/locales/en-US/gpuservice.ts | 13 ++-- src/locales/en-US/no-result.ts | 7 +-- src/locales/ja-JP/common.ts | 3 +- src/locales/ja-JP/gpuservice.ts | 11 ++-- src/locales/ja-JP/no-result.ts | 6 +- src/locales/ru-RU/common.ts | 3 +- src/locales/ru-RU/gpuservice.ts | 11 ++-- src/locales/ru-RU/no-result.ts | 8 +-- src/locales/tr-TR/common.ts | 3 +- src/locales/tr-TR/gpuservice.ts | 11 ++-- src/locales/tr-TR/no-result.ts | 9 +-- src/locales/zh-CN/common.ts | 3 +- src/locales/zh-CN/gpuservice.ts | 13 ++-- src/locales/zh-CN/no-result.ts | 6 +- .../instances/components/add-modal.tsx | 60 +++++++++++++------ .../components/instance-type-item.tsx | 40 ++++++++++--- .../gpu-service/instances/config/index.ts | 4 ++ .../gpu-service/instances/forms/index.tsx | 46 ++++++++++++-- .../instances/forms/instance-type.tsx | 16 ++++- .../instances/forms/template-selector.tsx | 2 +- .../instances/hooks/use-instances-columns.tsx | 2 +- src/pages/gpu-service/storage/apis/index.ts | 5 +- src/pages/gpu-service/storage/forms/basic.tsx | 15 +---- .../storage/hooks/use-storage-columns.tsx | 2 +- .../services/use-query-storage-class.ts | 4 +- .../templates/components/template-card.tsx | 22 +++---- .../gpu-service/templates/forms/basic.tsx | 49 ++++++++++++--- src/pages/resources/config/gpu-driver.ts | 47 +++++++++++---- src/request-config.tsx | 4 -- 33 files changed, 296 insertions(+), 137 deletions(-) diff --git a/.gitignore b/.gitignore index 3fb808ec..ba4ccb3d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ .DS_Store .idea .claude +/dist.zip diff --git a/src/atoms/user.ts b/src/atoms/user.ts index b3bdf5f0..e75d3067 100644 --- a/src/atoms/user.ts +++ b/src/atoms/user.ts @@ -36,5 +36,5 @@ export const getCurrentOrganizationId = (): string => { if (value != null) return String(value); } } catch {} - return 'gpustack-organization-1'; + return 'gpustack-default'; }; diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index 8ea8abe5..a6a9c89e 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -356,7 +356,7 @@ export default (props: any) => { config={{ apiBaseUrl: GPUSTACK_API_BASE_URL, theme: userSettings.theme, - iconUrl: '//at.alicdn.com/t/c/font_4613488_t4uxqkp138r.js', + iconUrl: '//at.alicdn.com/t/c/font_4613488_5prlfxmzgg5.js', isDarkTheme: userSettings.isDarkTheme, defaultColorPrimary: COLOR_PRIMARY }} diff --git a/src/locales/en-US/common.ts b/src/locales/en-US/common.ts index df7e5c19..12424437 100644 --- a/src/locales/en-US/common.ts +++ b/src/locales/en-US/common.ts @@ -280,5 +280,6 @@ export default { 'common.file.size.limit': 'File size must not exceed {size}.', 'common.file.format.limit': 'Invalid file format. Allowed: {formats}.', 'common.image.limit.width': 'Image width must be {width}.', - 'common.image.limit.height': 'Image height must be {height}.' + 'common.image.limit.height': 'Image height must be {height}.', + 'common.max': 'Max {count}' }; diff --git a/src/locales/en-US/gpuservice.ts b/src/locales/en-US/gpuservice.ts index be0cd514..6952183c 100644 --- a/src/locales/en-US/gpuservice.ts +++ b/src/locales/en-US/gpuservice.ts @@ -13,8 +13,8 @@ export default { 'gpuservice.template.command.placeholder': 'Separate arguments with spaces; wrap arguments containing spaces in quotes, e.g.: /bin/bash -c "echo hello world"', 'gpuservice.template.mountPath': 'Mount Path', - 'gpuservice.template.containerDisk': 'Container Disk (Gi)', - 'gpuservice.template.memory': 'Memory (Gi)', + 'gpuservice.template.containerDisk': 'Container Disk (GB)', + 'gpuservice.template.memory': 'Memory (GB)', 'gpuservice.template.ports': 'Ports', 'gpuservice.template.ports.add': 'Add Port', 'gpuservice.template.ports.invalid': @@ -41,13 +41,16 @@ export default { 'gpuservice.instance.section.basic': 'Basic Info', 'gpuservice.instance.section.type': 'Instance Type', 'gpuservice.instance.section.template': 'Instance Template', - 'gpuservice.instance.section.storage': 'Storage Volume', + 'gpuservice.instance.types': 'Instance Types', + 'gpuservice.instance.templates': 'Instance Templates', + 'gpuservice.instance.section.storage': 'Storage', 'gpuservice.instance.type.required': 'Please select an instance type', 'gpuservice.instance.gpuCount': 'GPU Count', 'gpuservice.instance.gpuCount.required': 'Please enter the GPU count', 'gpuservice.instance.gpuCount.max': 'The current instance type supports at most {count} GPU(s)', 'gpuservice.instance.stock': 'Stock', + 'gpuservice.instance.sliced': 'Sliced', 'gpuservice.instance.memory': 'Memory', 'gpuservice.instance.ram': 'RAM', 'gpuservice.instance.search.type.placeholder': @@ -68,8 +71,8 @@ export default { 'gpuservice.storage.type.object': 'Object Storage', 'gpuservice.storage.capacity': 'Capacity', 'gpuservice.storage.accessMode': 'Access Mode', - 'gpuservice.storage.persistent': 'Persistent Storage', - 'gpuservice.storage.temporary': 'Temporary Storage', + 'gpuservice.storage.persistent': 'Persistent', + 'gpuservice.storage.temporary': 'Temporary', 'gpuservice.storage.persistentVolume': 'Persistent Volume', 'gpuservice.storage.persistentVolume.required': 'Please select a persistent volume', diff --git a/src/locales/en-US/no-result.ts b/src/locales/en-US/no-result.ts index 6e80a8f6..d42fce82 100644 --- a/src/locales/en-US/no-result.ts +++ b/src/locales/en-US/no-result.ts @@ -55,15 +55,14 @@ export default { 'noresult.instances.nofound': 'No matching instances found.', 'noresult.gpuservice.template.title': 'No Instance Templates', 'noresult.gpuservice.template.subTitle': - 'Once you create an instance template, it will appear here.', + 'No instance templates have been added yet.', 'noresult.gpuservice.template.nofound': 'No matching instance templates found.', 'noresult.gpuservice.instance.title': 'No GPU Instances', 'noresult.gpuservice.instance.subTitle': - 'Once you create a GPU instance, it will appear here.', + 'No GPU instances have been added yet.', 'noresult.gpuservice.instance.nofound': 'No matching GPU instances found.', 'noresult.gpuservice.storage.title': 'No Storage', - 'noresult.gpuservice.storage.subTitle': - 'Once you create a storage, it will appear here.', + 'noresult.gpuservice.storage.subTitle': 'No storage has been added yet.', 'noresult.gpuservice.storage.nofound': 'No matching storage found.' }; diff --git a/src/locales/ja-JP/common.ts b/src/locales/ja-JP/common.ts index 4058912d..b62562cd 100644 --- a/src/locales/ja-JP/common.ts +++ b/src/locales/ja-JP/common.ts @@ -280,7 +280,8 @@ export default { 'common.file.size.limit': 'File size must not exceed {size}.', 'common.file.format.limit': 'Invalid file format. Allowed: {formats}.', 'common.image.limit.width': 'Image width must be {width}.', - 'common.image.limit.height': 'Image height must be {height}.' + 'common.image.limit.height': 'Image height must be {height}.', + 'common.max': '最大 {count}' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== diff --git a/src/locales/ja-JP/gpuservice.ts b/src/locales/ja-JP/gpuservice.ts index 7b945784..e9b2730d 100644 --- a/src/locales/ja-JP/gpuservice.ts +++ b/src/locales/ja-JP/gpuservice.ts @@ -13,8 +13,8 @@ export default { 'gpuservice.template.command.placeholder': '引数はスペースで区切り、スペースを含む引数は引用符で囲んでください。例:/bin/bash -c "echo hello world"', 'gpuservice.template.mountPath': 'マウントパス', - 'gpuservice.template.containerDisk': 'コンテナディスク (Gi)', - 'gpuservice.template.memory': 'メモリ (Gi)', + 'gpuservice.template.containerDisk': 'コンテナディスク (GB)', + 'gpuservice.template.memory': 'メモリ (GB)', 'gpuservice.template.ports': 'ポート', 'gpuservice.template.ports.add': 'ポートを追加', 'gpuservice.template.ports.invalid': 'ポート設定を完成させてください。', @@ -39,6 +39,8 @@ export default { 'gpuservice.instance.section.basic': '基本情報', 'gpuservice.instance.section.type': 'インスタンスタイプ', 'gpuservice.instance.section.template': 'インスタンステンプレート', + 'gpuservice.instance.types': 'インスタンスタイプ', + 'gpuservice.instance.templates': 'インスタンステンプレート', 'gpuservice.instance.section.storage': 'ストレージボリューム', 'gpuservice.instance.type.required': 'インスタンスタイプを選択してください', 'gpuservice.instance.gpuCount': 'GPU 数', @@ -46,6 +48,7 @@ export default { 'gpuservice.instance.gpuCount.max': '現在のインスタンスタイプは最大 {count} 個の GPU をサポートします', 'gpuservice.instance.stock': '在庫', + 'gpuservice.instance.sliced': '分割', 'gpuservice.instance.memory': 'Memory', 'gpuservice.instance.ram': 'RAM', 'gpuservice.instance.search.type.placeholder': @@ -66,8 +69,8 @@ export default { 'gpuservice.storage.type.object': 'オブジェクトストレージ', 'gpuservice.storage.capacity': '容量', 'gpuservice.storage.accessMode': 'アクセスモード', - 'gpuservice.storage.persistent': '永続ストレージ', - 'gpuservice.storage.temporary': '一時ストレージ', + 'gpuservice.storage.persistent': '永続', + 'gpuservice.storage.temporary': '一時', 'gpuservice.storage.persistentVolume': '永続ボリューム', 'gpuservice.storage.persistentVolume.required': '永続ボリュームを選択してください', diff --git a/src/locales/ja-JP/no-result.ts b/src/locales/ja-JP/no-result.ts index 8848ef97..2fd7ec4c 100644 --- a/src/locales/ja-JP/no-result.ts +++ b/src/locales/ja-JP/no-result.ts @@ -55,16 +55,16 @@ export default { 'noresult.instances.nofound': 'No matching instances found.', 'noresult.gpuservice.template.title': 'インスタンステンプレートなし', 'noresult.gpuservice.template.subTitle': - 'インスタンステンプレートを作成すると、ここに表示されます。', + 'インスタンステンプレートはまだ追加されていません。', 'noresult.gpuservice.template.nofound': '一致するインスタンステンプレートが見つかりません。', 'noresult.gpuservice.instance.title': 'GPU インスタンスなし', 'noresult.gpuservice.instance.subTitle': - 'GPU インスタンスを作成すると、ここに表示されます。', + 'GPU インスタンスはまだ追加されていません。', 'noresult.gpuservice.instance.nofound': '一致する GPU インスタンスが見つかりません。', 'noresult.gpuservice.storage.title': 'ストレージなし', 'noresult.gpuservice.storage.subTitle': - 'ストレージを作成すると、ここに表示されます。', + 'ストレージはまだ追加されていません。', 'noresult.gpuservice.storage.nofound': '一致するストレージが見つかりません。' }; diff --git a/src/locales/ru-RU/common.ts b/src/locales/ru-RU/common.ts index 002f2e8b..94d6889d 100644 --- a/src/locales/ru-RU/common.ts +++ b/src/locales/ru-RU/common.ts @@ -279,7 +279,8 @@ export default { 'common.file.size.limit': 'File size must not exceed {size}.', 'common.file.format.limit': 'Invalid file format. Allowed: {formats}.', 'common.image.limit.width': 'Image width must be {width}.', - 'common.image.limit.height': 'Image height must be {height}.' + 'common.image.limit.height': 'Image height must be {height}.', + 'common.max': 'Макс. {count}' }; // ========== To-Do: Translate Keys (Remove After Translation) ========== diff --git a/src/locales/ru-RU/gpuservice.ts b/src/locales/ru-RU/gpuservice.ts index 2842ed1c..8df0092e 100644 --- a/src/locales/ru-RU/gpuservice.ts +++ b/src/locales/ru-RU/gpuservice.ts @@ -14,8 +14,8 @@ export default { 'gpuservice.template.command.placeholder': 'Разделяйте аргументы пробелами; аргументы с пробелами заключайте в кавычки, например: /bin/bash -c "echo hello world"', 'gpuservice.template.mountPath': 'Путь монтирования', - 'gpuservice.template.containerDisk': 'Диск контейнера (Gi)', - 'gpuservice.template.memory': 'Память (Gi)', + 'gpuservice.template.containerDisk': 'Диск контейнера (GB)', + 'gpuservice.template.memory': 'Память (GB)', 'gpuservice.template.ports': 'Порты', 'gpuservice.template.ports.add': 'Добавить порт', 'gpuservice.template.ports.invalid': 'Заполните настройки портов полностью.', @@ -41,6 +41,8 @@ export default { 'gpuservice.instance.section.basic': 'Основная информация', 'gpuservice.instance.section.type': 'Тип экземпляра', 'gpuservice.instance.section.template': 'Шаблон экземпляра', + 'gpuservice.instance.types': 'Типы экземпляров', + 'gpuservice.instance.templates': 'Шаблоны экземпляров', 'gpuservice.instance.section.storage': 'Том хранилища', 'gpuservice.instance.type.required': 'Выберите тип экземпляра', 'gpuservice.instance.gpuCount': 'Количество GPU', @@ -48,6 +50,7 @@ export default { 'gpuservice.instance.gpuCount.max': 'Текущий тип экземпляра поддерживает максимум {count} GPU', 'gpuservice.instance.stock': 'Остаток', + 'gpuservice.instance.sliced': 'Разделено', 'gpuservice.instance.memory': 'Память', 'gpuservice.instance.ram': 'RAM', 'gpuservice.instance.search.type.placeholder': @@ -68,8 +71,8 @@ export default { 'gpuservice.storage.type.object': 'Объектное хранилище', 'gpuservice.storage.capacity': 'Объём', 'gpuservice.storage.accessMode': 'Режим доступа', - 'gpuservice.storage.persistent': 'Постоянное хранилище', - 'gpuservice.storage.temporary': 'Временное хранилище', + 'gpuservice.storage.persistent': 'Постоянное', + 'gpuservice.storage.temporary': 'Временное', 'gpuservice.storage.persistentVolume': 'Постоянный том', 'gpuservice.storage.persistentVolume.required': 'Выберите постоянный том', 'gpuservice.storage.tempCapacity': 'Объём хранилища (ГБ)', diff --git a/src/locales/ru-RU/no-result.ts b/src/locales/ru-RU/no-result.ts index 6d561742..c8de8586 100644 --- a/src/locales/ru-RU/no-result.ts +++ b/src/locales/ru-RU/no-result.ts @@ -56,17 +56,15 @@ export default { 'noresult.instances.nofound': 'No matching instances found.', 'noresult.gpuservice.template.title': 'Нет шаблонов экземпляров', 'noresult.gpuservice.template.subTitle': - 'После создания шаблона экземпляра он появится здесь.', + 'Шаблоны экземпляров ещё не добавлены.', 'noresult.gpuservice.template.nofound': 'Подходящие шаблоны экземпляров не найдены.', 'noresult.gpuservice.instance.title': 'Нет экземпляров GPU', - 'noresult.gpuservice.instance.subTitle': - 'После создания экземпляра GPU он появится здесь.', + 'noresult.gpuservice.instance.subTitle': 'Экземпляры GPU ещё не добавлены.', 'noresult.gpuservice.instance.nofound': 'Подходящие экземпляры GPU не найдены.', 'noresult.gpuservice.storage.title': 'Нет хранилищ', - 'noresult.gpuservice.storage.subTitle': - 'После создания хранилища оно появится здесь.', + 'noresult.gpuservice.storage.subTitle': 'Хранилища ещё не добавлены.', 'noresult.gpuservice.storage.nofound': 'Подходящие хранилища не найдены.' }; diff --git a/src/locales/tr-TR/common.ts b/src/locales/tr-TR/common.ts index a45f3779..2d198dd8 100644 --- a/src/locales/tr-TR/common.ts +++ b/src/locales/tr-TR/common.ts @@ -283,5 +283,6 @@ export default { 'common.file.size.limit': 'File size must not exceed {size}.', 'common.file.format.limit': 'Invalid file format. Allowed: {formats}.', 'common.image.limit.width': 'Image width must be {width}.', - 'common.image.limit.height': 'Image height must be {height}.' + 'common.image.limit.height': 'Image height must be {height}.', + 'common.max': 'Maks. {count}' }; diff --git a/src/locales/tr-TR/gpuservice.ts b/src/locales/tr-TR/gpuservice.ts index 046e87ee..8c77ebdd 100644 --- a/src/locales/tr-TR/gpuservice.ts +++ b/src/locales/tr-TR/gpuservice.ts @@ -13,8 +13,8 @@ export default { 'gpuservice.template.command.placeholder': 'Argümanları boşlukla ayırın; boşluk içeren argümanları tırnak içine alın, örn.: /bin/bash -c "echo hello world"', 'gpuservice.template.mountPath': 'Bağlama Yolu', - 'gpuservice.template.containerDisk': 'Konteyner Diski (Gi)', - 'gpuservice.template.memory': 'Bellek (Gi)', + 'gpuservice.template.containerDisk': 'Konteyner Diski (GB)', + 'gpuservice.template.memory': 'Bellek (GB)', 'gpuservice.template.ports': 'Bağlantı Noktaları', 'gpuservice.template.ports.add': 'Bağlantı Noktası Ekle', 'gpuservice.template.ports.invalid': @@ -40,6 +40,8 @@ export default { 'gpuservice.instance.section.basic': 'Temel Bilgiler', 'gpuservice.instance.section.type': 'Örnek Türü', 'gpuservice.instance.section.template': 'Örnek Şablonu', + 'gpuservice.instance.types': 'Örnek Türleri', + 'gpuservice.instance.templates': 'Örnek Şablonları', 'gpuservice.instance.section.storage': 'Depolama Hacmi', 'gpuservice.instance.type.required': 'Lütfen bir örnek türü seçin', 'gpuservice.instance.gpuCount': 'GPU Sayısı', @@ -47,6 +49,7 @@ export default { 'gpuservice.instance.gpuCount.max': 'Mevcut örnek türü en fazla {count} GPU destekler', 'gpuservice.instance.stock': 'Stok', + 'gpuservice.instance.sliced': 'Bölünmüş', 'gpuservice.instance.memory': 'Bellek', 'gpuservice.instance.ram': 'RAM', 'gpuservice.instance.search.type.placeholder': @@ -67,8 +70,8 @@ export default { 'gpuservice.storage.type.object': 'Nesne Depolama', 'gpuservice.storage.capacity': 'Kapasite', 'gpuservice.storage.accessMode': 'Erişim Modu', - 'gpuservice.storage.persistent': 'Kalıcı Depolama', - 'gpuservice.storage.temporary': 'Geçici Depolama', + 'gpuservice.storage.persistent': 'Kalıcı', + 'gpuservice.storage.temporary': 'Geçici', 'gpuservice.storage.persistentVolume': 'Kalıcı Hacim', 'gpuservice.storage.persistentVolume.required': 'Lütfen bir kalıcı hacim seçin', diff --git a/src/locales/tr-TR/no-result.ts b/src/locales/tr-TR/no-result.ts index f8cb07fb..46237449 100644 --- a/src/locales/tr-TR/no-result.ts +++ b/src/locales/tr-TR/no-result.ts @@ -56,16 +56,13 @@ export default { 'There are no running model instances. Deploy a model to create one automatically.', 'noresult.instances.nofound': 'No matching instances found.', 'noresult.gpuservice.template.title': 'Örnek Şablonu Yok', - 'noresult.gpuservice.template.subTitle': - 'Bir örnek şablonu oluşturduğunuzda burada görünecek.', + 'noresult.gpuservice.template.subTitle': 'Henüz örnek şablonu eklenmedi.', 'noresult.gpuservice.template.nofound': 'Eşleşen örnek şablonu bulunamadı.', 'noresult.gpuservice.instance.title': 'GPU Örneği Yok', - 'noresult.gpuservice.instance.subTitle': - 'Bir GPU örneği oluşturduğunuzda burada görünecek.', + 'noresult.gpuservice.instance.subTitle': 'Henüz GPU örneği eklenmedi.', 'noresult.gpuservice.instance.nofound': 'Eşleşen GPU örneği bulunamadı.', 'noresult.gpuservice.storage.title': 'Depolama Yok', - 'noresult.gpuservice.storage.subTitle': - 'Bir depolama oluşturduğunuzda burada görünecek.', + 'noresult.gpuservice.storage.subTitle': 'Henüz depolama eklenmedi.', 'noresult.gpuservice.storage.nofound': 'Eşleşen depolama bulunamadı.' }; diff --git a/src/locales/zh-CN/common.ts b/src/locales/zh-CN/common.ts index 0398d75a..ffe4dc80 100644 --- a/src/locales/zh-CN/common.ts +++ b/src/locales/zh-CN/common.ts @@ -272,5 +272,6 @@ export default { 'common.file.size.limit': '文件大小不超过{size}。', 'common.file.format.limit': '文件格式不正确,仅支持{formats}。', 'common.image.limit.width': '图片宽度须为{width}。', - 'common.image.limit.height': '图片高度须为{height}。' + 'common.image.limit.height': '图片高度须为{height}。', + 'common.max': '最大 {count}' }; diff --git a/src/locales/zh-CN/gpuservice.ts b/src/locales/zh-CN/gpuservice.ts index f71874a6..ed2e20e6 100644 --- a/src/locales/zh-CN/gpuservice.ts +++ b/src/locales/zh-CN/gpuservice.ts @@ -13,8 +13,8 @@ export default { 'gpuservice.template.command.placeholder': '使用空格分隔参数;含空格的参数请用引号包裹,例如:/bin/bash -c "echo hello world"', 'gpuservice.template.mountPath': '挂载路径', - 'gpuservice.template.containerDisk': '容器磁盘(Gi)', - 'gpuservice.template.memory': '内存(Gi)', + 'gpuservice.template.containerDisk': '容器磁盘(GB)', + 'gpuservice.template.memory': '内存(GB)', 'gpuservice.template.ports': '端口', 'gpuservice.template.ports.add': '添加端口', 'gpuservice.template.ports.invalid': '请填写完整的端口配置', @@ -29,7 +29,7 @@ export default { 'gpuservice.template.card.ports': '端口', 'gpuservice.publicKey.label': 'SSH 公钥', 'gpuservice.publicKey.placeholder': - '以 ssh-rsa 或 ssh-ed25519 开头,每个 Public Key 单独一行\n\n查看 Public Key:\n- RSA\ncat ~/.ssh/id_rsa.pub\n- Ed25519\ncat ~/.ssh/id_ed25519.pub', + '以 ssh-rsa 或 ssh-ed25519 开头,每个公钥单独一行\n\n查看公钥:\n- RSA\ncat ~/.ssh/id_rsa.pub\n- Ed25519\ncat ~/.ssh/id_ed25519.pub', 'gpuservice.instance': 'GPU 实例', 'gpuservice.instance.add': '添加 GPU 实例', 'gpuservice.instance.edit': '编辑 GPU 实例', @@ -39,12 +39,15 @@ export default { 'gpuservice.instance.section.basic': '基础信息', 'gpuservice.instance.section.type': '实例类型', 'gpuservice.instance.section.template': '实例模板', + 'gpuservice.instance.types': '实例类型', + 'gpuservice.instance.templates': '实例模板', 'gpuservice.instance.section.storage': '存储卷', 'gpuservice.instance.type.required': '请选择实例类型', 'gpuservice.instance.gpuCount': 'GPU 数量', 'gpuservice.instance.gpuCount.required': '请输入 GPU 数量', 'gpuservice.instance.gpuCount.max': '当前实例类型最多支持 {count} 个 GPU', 'gpuservice.instance.stock': '库存', + 'gpuservice.instance.sliced': '切分', 'gpuservice.instance.memory': '显存', 'gpuservice.instance.ram': '内存', 'gpuservice.instance.search.type.placeholder': '搜索名称、显存、内存或 vCPU', @@ -64,8 +67,8 @@ export default { 'gpuservice.storage.type.object': '对象存储', 'gpuservice.storage.capacity': '容量', 'gpuservice.storage.accessMode': '访问模式', - 'gpuservice.storage.persistent': '持久存储', - 'gpuservice.storage.temporary': '临时存储', + 'gpuservice.storage.persistent': '持久', + 'gpuservice.storage.temporary': '临时', 'gpuservice.storage.persistentVolume': '持久卷', 'gpuservice.storage.persistentVolume.required': '请选择持久卷', 'gpuservice.storage.tempCapacity': '存储容量 (GB)', diff --git a/src/locales/zh-CN/no-result.ts b/src/locales/zh-CN/no-result.ts index 271574f2..b1f357b8 100644 --- a/src/locales/zh-CN/no-result.ts +++ b/src/locales/zh-CN/no-result.ts @@ -52,12 +52,12 @@ export default { '当前没有运行中的模型实例,部署模型后将自动创建。', 'noresult.instances.nofound': '未找到匹配的实例', 'noresult.gpuservice.template.title': '暂无实例模板', - 'noresult.gpuservice.template.subTitle': '创建一个实例模板后会显示在这里', + 'noresult.gpuservice.template.subTitle': '尚未添加任何实例模板。', 'noresult.gpuservice.template.nofound': '未找到匹配的实例模板', 'noresult.gpuservice.instance.title': '暂无 GPU 实例', - 'noresult.gpuservice.instance.subTitle': '创建一个 GPU 实例后会显示在这里', + 'noresult.gpuservice.instance.subTitle': '尚未添加任何 GPU 实例。', 'noresult.gpuservice.instance.nofound': '未找到匹配的 GPU 实例', 'noresult.gpuservice.storage.title': '暂无存储', - 'noresult.gpuservice.storage.subTitle': '创建一个存储后会显示在这里', + 'noresult.gpuservice.storage.subTitle': '尚未添加任何存储。', 'noresult.gpuservice.storage.nofound': '未找到匹配的存储' }; diff --git a/src/pages/gpu-service/instances/components/add-modal.tsx b/src/pages/gpu-service/instances/components/add-modal.tsx index 6b1f082e..2c5a2033 100644 --- a/src/pages/gpu-service/instances/components/add-modal.tsx +++ b/src/pages/gpu-service/instances/components/add-modal.tsx @@ -24,7 +24,7 @@ const Container = styled.div` const ColWrapper = styled.div` display: flex; flex: 1; - max-width: 33.33%; + max-width: 33%; min-height: 0; `; @@ -39,7 +39,7 @@ const PanelBody = styled.div` const FormWrapper = styled.div` display: flex; flex: 1; - max-width: 33.33%; + max-width: 34%; min-height: 0; `; @@ -52,6 +52,30 @@ type AddModalProps = { onCancel: () => void; }; +const ColTitle: React.FC<{ + children: React.ReactNode; + style?: React.CSSProperties; +}> = ({ children, style }) => { + return ( + + {children} + + ); +}; + const AddModal: React.FC = ({ title, action, @@ -81,7 +105,7 @@ const AddModal: React.FC = ({ if (open) { autoSelectedRef.current = false; fetchData({}); - fetchTemplates({ page: 1, perPage: 100 }); + fetchTemplates({ page: -1 }); } else { setSelectedInstanceType(undefined); setSelectedManufacturer(undefined); @@ -112,6 +136,11 @@ const AddModal: React.FC = ({ }, [instanceKeyword, instanceTypeList]); const manufacturerMatchedTemplates = useMemo(() => { + console.log( + 'Filtering templates by manufacturer:', + templateList, + selectedManufacturer + ); if (!selectedManufacturer) { return templateList; } @@ -277,6 +306,9 @@ const AddModal: React.FC = ({ + + {intl.formatMessage({ id: 'gpuservice.instance.types' })} +
= ({ + + {intl.formatMessage({ id: 'gpuservice.instance.templates' })} +
= ({ } > <> - - Configuration - + + {intl.formatMessage({ id: 'common.title.config' })} + diff --git a/src/pages/gpu-service/instances/components/instance-type-item.tsx b/src/pages/gpu-service/instances/components/instance-type-item.tsx index bc2de5b9..9b96eef2 100644 --- a/src/pages/gpu-service/instances/components/instance-type-item.tsx +++ b/src/pages/gpu-service/instances/components/instance-type-item.tsx @@ -115,16 +115,38 @@ const InstanceTypeItem: React.FC = ({ {convertKiToGi(item.spec?.memory) ?? '-'} - - - - - - {intl.formatMessage({ id: 'gpuservice.instance.stock' })} + {item.spec?.sliced && ( + <> + + + + + + {intl.formatMessage({ + id: 'gpuservice.instance.sliced' + })} + + {item.spec?.sliced ?? '-'} + - {item.status?.accelerator?.remaining ?? '-'} - - + + )} + {item.status?.accelerator?.remaining && ( + <> + + + + + + {intl.formatMessage({ + id: 'gpuservice.instance.stock' + })} + + {item.status?.accelerator?.remaining ?? '-'} + + + + )} )} diff --git a/src/pages/gpu-service/instances/config/index.ts b/src/pages/gpu-service/instances/config/index.ts index 6a119caa..9d3d1b38 100644 --- a/src/pages/gpu-service/instances/config/index.ts +++ b/src/pages/gpu-service/instances/config/index.ts @@ -130,6 +130,10 @@ export const transformInstanceType = ( const status = item.status; return { ...item, + spec: { + ...item.spec, + manufacturer: !item.spec?.acceleratable ? 'cpu' : item.spec.manufacturer + }, status: { ...status, accelerator: transformInstanceTypeResource( diff --git a/src/pages/gpu-service/instances/forms/index.tsx b/src/pages/gpu-service/instances/forms/index.tsx index d6fbd169..2435521c 100644 --- a/src/pages/gpu-service/instances/forms/index.tsx +++ b/src/pages/gpu-service/instances/forms/index.tsx @@ -20,8 +20,10 @@ import { } from 'react'; import useGetSshkey from '../../public-keys/services/use-get-sshkey'; import { DefaultImagePullPolicy } from '../../templates/config'; -import TemplateBasicForm from '../../templates/forms/basic'; -import { FormData, InstancePort, ListItem } from '../config/types'; +import TemplateBasicForm, { + BasicResourceMax +} from '../../templates/forms/basic'; +import { FormData, InstanceTypeItem, ListItem } from '../config/types'; import Basic from './basic'; import InstanceTypeFormItem from './instance-type'; import StorageVolume from './storage-volume'; @@ -39,9 +41,18 @@ interface InstanceFormProps { action: PageActionType; currentData?: ListItem | null; namespace?: string; + instanceTypeList?: InstanceTypeItem[]; onFinish: (values: FormData) => Promise; } +const parseQuantityToNumber = (value?: string): number | null => { + if (!value) return null; + const match = /^(-?\d+(?:\.\d+)?)/.exec(String(value)); + if (!match) return null; + const num = Number(match[1]); + return Number.isFinite(num) && num > 0 ? num : null; +}; + const TABKeysMap = { BASIC: 'basic', INSTANCE_TYPE: 'instanceType', @@ -75,6 +86,7 @@ const GPUServiceInstanceForm: React.FC = forwardRef( currentData, open, namespace = 'default', + instanceTypeList = [], onFinish } = props; const intl = useIntl(); @@ -108,7 +120,7 @@ const GPUServiceInstanceForm: React.FC = forwardRef( { value: TABKeysMap.BASIC, label: intl.formatMessage({ - id: 'gpuservice.instance.section.basic' + id: 'common.title.basicInfo' }), icon: , field: 'name' @@ -139,14 +151,31 @@ const GPUServiceInstanceForm: React.FC = forwardRef( [intl] ); - const ports = (Form.useWatch(['spec', 'ports'], form) || - []) as InstancePort[]; + const ports = Form.useWatch(['spec', 'ports'], form) || []; const hasSshPort = useMemo( () => ports.some((p) => Number(p?.port) === SSH_PORT && p.protocol === 'TCP'), [ports] ); + const selectedTypeName = Form.useWatch(['spec', 'type'], form) as + | string + | undefined; + + const onceMaxRequest = useMemo(() => { + const selected = instanceTypeList.find( + (item) => (item.metadata?.name || '') === selectedTypeName + ); + const status = selected?.status; + return { + cpu: parseQuantityToNumber(status?.cpu?.onceMaxRequest), + memory: parseQuantityToNumber(status?.ram?.onceMaxRequest), + localStorage: parseQuantityToNumber( + status?.localStorage?.onceMaxRequest + ) + }; + }, [instanceTypeList, selectedTypeName]); + const onTargetChange = (key: string) => { scrollTabsRef.current?.handleTargetChange(key); }; @@ -277,7 +306,12 @@ const GPUServiceInstanceForm: React.FC = forwardRef( id: 'gpuservice.instance.section.template' }), forceRender: true, - children: + children: ( + + ) }, { key: TABKeysMap.STORAGE, diff --git a/src/pages/gpu-service/instances/forms/instance-type.tsx b/src/pages/gpu-service/instances/forms/instance-type.tsx index 6a0d60e5..fbc3943a 100644 --- a/src/pages/gpu-service/instances/forms/instance-type.tsx +++ b/src/pages/gpu-service/instances/forms/instance-type.tsx @@ -1,6 +1,6 @@ import { InputNumber as CInputNumber } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; -import { Form } from 'antd'; +import { Flex, Form } from 'antd'; import { useEffect, useMemo } from 'react'; import styled from 'styled-components'; import InstanceTypeItem from '../components/instance-type-item'; @@ -138,7 +138,19 @@ const InstanceTypeFormItem = () => { min={1} max={maxGpuCount} precision={0} - label={intl.formatMessage({ id: 'gpuservice.instance.gpuCount' })} + label={ + + {intl.formatMessage({ id: 'gpuservice.instance.gpuCount' })} + + ( + {intl.formatMessage( + { id: 'common.max' }, + { count: maxGpuCount } + )} + ) + + + } required /> diff --git a/src/pages/gpu-service/instances/forms/template-selector.tsx b/src/pages/gpu-service/instances/forms/template-selector.tsx index 6d3e5a1b..704cbcb8 100644 --- a/src/pages/gpu-service/instances/forms/template-selector.tsx +++ b/src/pages/gpu-service/instances/forms/template-selector.tsx @@ -33,7 +33,7 @@ const TemplateContent = styled.div` } .value { - color: var(--ant-color-text); + color: var(--ant-color-text-secondary); } .icon { diff --git a/src/pages/gpu-service/instances/hooks/use-instances-columns.tsx b/src/pages/gpu-service/instances/hooks/use-instances-columns.tsx index 979eec5c..01db6711 100644 --- a/src/pages/gpu-service/instances/hooks/use-instances-columns.tsx +++ b/src/pages/gpu-service/instances/hooks/use-instances-columns.tsx @@ -134,7 +134,7 @@ const useInstancesColumns = ({ backgroundColor: 'var(--ant-color-fill-tertiary)' }} > - {entry.port} + {entry.port} diff --git a/src/pages/gpu-service/storage/apis/index.ts b/src/pages/gpu-service/storage/apis/index.ts index 514903c7..401d5025 100644 --- a/src/pages/gpu-service/storage/apis/index.ts +++ b/src/pages/gpu-service/storage/apis/index.ts @@ -116,7 +116,10 @@ export async function queryStorageClass( { method: 'GET', params: omitPathParams(params), - cancelToken: options?.token + cancelToken: options?.token, + headers: { + skipErrorHandler: 'true' + } } ); } diff --git a/src/pages/gpu-service/storage/forms/basic.tsx b/src/pages/gpu-service/storage/forms/basic.tsx index af8cf42f..676d76a4 100644 --- a/src/pages/gpu-service/storage/forms/basic.tsx +++ b/src/pages/gpu-service/storage/forms/basic.tsx @@ -28,10 +28,7 @@ const Basic = ({ open }: { open: boolean }) => { rules={[ { required: true, - message: getRuleMessage( - 'input', - intl.formatMessage({ id: 'common.table.name' }) - ) + message: getRuleMessage('input', 'common.table.name') } ]} > @@ -47,10 +44,7 @@ const Basic = ({ open }: { open: boolean }) => { rules={[ { required: true, - message: getRuleMessage( - 'select', - intl.formatMessage({ id: 'gpuservice.storage.type' }) - ) + message: getRuleMessage('select', 'gpuservice.storage.type') } ]} > @@ -72,10 +66,7 @@ const Basic = ({ open }: { open: boolean }) => { rules={[ { required: true, - message: getRuleMessage( - 'input', - intl.formatMessage({ id: 'gpuservice.storage.capacity' }) - ) + message: getRuleMessage('input', 'gpuservice.storage.capacity') } ]} > diff --git a/src/pages/gpu-service/storage/hooks/use-storage-columns.tsx b/src/pages/gpu-service/storage/hooks/use-storage-columns.tsx index 7146ecff..2701aee4 100644 --- a/src/pages/gpu-service/storage/hooks/use-storage-columns.tsx +++ b/src/pages/gpu-service/storage/hooks/use-storage-columns.tsx @@ -52,7 +52,7 @@ const useStorageColumns = ({ dataIndex: ['spec', 'capacity'], key: 'capacity', sorter: false, - render: (value: string) => value ?? '-' + render: (value: string) => (value ? value.replace(/Gi$/, 'GB') : '-') }, { title: intl.formatMessage({ id: 'common.table.status' }), diff --git a/src/pages/gpu-service/storage/services/use-query-storage-class.ts b/src/pages/gpu-service/storage/services/use-query-storage-class.ts index 1c63f4f4..94fc5132 100644 --- a/src/pages/gpu-service/storage/services/use-query-storage-class.ts +++ b/src/pages/gpu-service/storage/services/use-query-storage-class.ts @@ -21,8 +21,8 @@ export default function useQueryStorageClass() { >({ key: 'storageClass', fetchList, - getLabel: (item) => item.metadata.name, - getValue: (item) => item.metadata.name + getLabel: (item) => item?.metadata.name, + getValue: (item) => item?.metadata.name }); return { diff --git a/src/pages/gpu-service/templates/components/template-card.tsx b/src/pages/gpu-service/templates/components/template-card.tsx index a7e58aeb..32da7994 100644 --- a/src/pages/gpu-service/templates/components/template-card.tsx +++ b/src/pages/gpu-service/templates/components/template-card.tsx @@ -7,7 +7,7 @@ import mooreLogo from '@/assets/logo/moore-logo.png'; import nvidiaLogo from '@/assets/logo/nvidia.png'; import theadLogoEN from '@/assets/logo/t-head-en.png'; import theadLogoZH from '@/assets/logo/t-head-zh.png'; -import { GPUDriverMap } from '@/pages/resources/config/gpu-driver'; +import { manfacturerValueMap } from '@/pages/resources/config/gpu-driver'; import { AutoTooltip, DropdownActions, @@ -73,7 +73,7 @@ const InfoItem = styled.div` color: var(--ant-color-text-quaternary); } .value { - color: var(--ant-color-text); + color: var(--ant-color-text-secondary); } `; @@ -99,28 +99,28 @@ const TemplateCardItem: React.FC = ({ data, onSelect }) => { const renderLogo = () => { switch (data.manufacturer) { - case GPUDriverMap.NVIDIA: + case manfacturerValueMap.NVIDIA: return ; - case GPUDriverMap.AMD: + case manfacturerValueMap.AMD: return ( ); - case GPUDriverMap.ASCEND: + case manfacturerValueMap.ASCEND: return ; - case GPUDriverMap.HYGON: + case manfacturerValueMap.HYGON: return ; - case GPUDriverMap.METAX: + case manfacturerValueMap.METAX: return ; - case GPUDriverMap.MOORE_THREADS: + case manfacturerValueMap.MOORE_THREADS: return ; - case GPUDriverMap.ILUVATAR: + case manfacturerValueMap.ILUVATAR: return ; - case GPUDriverMap.CAMBRICON: + case manfacturerValueMap.CAMBRICON: return ; - case GPUDriverMap.THEAD: + case manfacturerValueMap.THEAD: return ( = ({ page = 'template' }) => { +interface BasicProps { + page?: 'template' | 'instance'; + onceMaxRequest?: BasicResourceMax; +} + +const Basic: React.FC = ({ page = 'template', onceMaxRequest }) => { const { getRuleMessage } = useAppUtils(); const intl = useIntl(); @@ -30,11 +37,26 @@ const Basic: React.FC = ({ page = 'template' }) => { () => Object.values(GPUsConfigs).map((item) => ({ label: item.label, - value: item.value + value: item.gpuVendor })), [] ); + const renderMaxLabel = ( + label: React.ReactNode, + max?: number | null + ): React.ReactNode => { + if (max == null) return label; + return ( + + {label} + + ({intl.formatMessage({ id: 'common.max' }, { count: max })}) + + + ); + }; + return ( <>
@@ -135,9 +157,11 @@ const Basic: React.FC = ({ page = 'template' }) => { })} >
@@ -149,7 +173,10 @@ const Basic: React.FC = ({ page = 'template' }) => { normalize={(value) => (value ? `${value}` : '')} getValueProps={(value) => ({ value: value ? String(value) : '' })} > - +
@@ -161,7 +188,11 @@ const Basic: React.FC = ({ page = 'template' }) => { })} >
diff --git a/src/pages/resources/config/gpu-driver.ts b/src/pages/resources/config/gpu-driver.ts index 8370b1e2..a066ee7e 100644 --- a/src/pages/resources/config/gpu-driver.ts +++ b/src/pages/resources/config/gpu-driver.ts @@ -24,63 +24,90 @@ export const ManufacturerMap = { [GPUDriverMap.THEAD]: 'vendor.thead' }; +export const manfacturerValueMap = { + NVIDIA: 'nvidia', + AMD: 'amd', + ASCEND: 'ascend', + HYGON: 'hygon', + MOORE_THREADS: 'moorthreads', + ILUVATAR: 'iluvatar', + CAMBRICON: 'cambricon', + METAX: 'metax', + THEAD: 'thead' +}; + export const GPUsConfigs: Record< string, - { label: string; value: string; runtime: string; driver: string } + { + label: string; + value: string; + runtime: string; + driver: string; + gpuVendor?: string; + } > = { [GPUDriverMap.NVIDIA]: { label: 'NVIDIA', // this label is used in echo command, do not translate value: GPUDriverMap.NVIDIA, runtime: 'nvidia', - driver: 'nvidia-smi' + driver: 'nvidia-smi', + gpuVendor: 'nvidia' }, [GPUDriverMap.AMD]: { label: 'AMD', value: GPUDriverMap.AMD, runtime: 'amd', - driver: 'amd-smi static' + driver: 'amd-smi static', + gpuVendor: 'amd' }, [GPUDriverMap.ASCEND]: { label: 'Ascend', value: GPUDriverMap.ASCEND, runtime: 'ascend', - driver: 'npu-smi info' + driver: 'npu-smi info', + gpuVendor: 'ascend' }, [GPUDriverMap.HYGON]: { label: 'Hygon', value: GPUDriverMap.HYGON, runtime: '', // TODO: confirm runtime name - driver: 'hy-smi' + driver: 'hy-smi', + gpuVendor: 'hygon' }, [GPUDriverMap.MOORE_THREADS]: { label: 'Moore Threads', value: GPUDriverMap.MOORE_THREADS, runtime: 'mthreads', - driver: 'mthreads-gmi' + driver: 'mthreads-gmi', + gpuVendor: 'mthreads' }, [GPUDriverMap.ILUVATAR]: { label: 'Iluvatar', value: GPUDriverMap.ILUVATAR, runtime: 'iluvatar', - driver: 'ixsmi' + driver: 'ixsmi', + gpuVendor: 'iluvatar' }, [GPUDriverMap.CAMBRICON]: { label: 'Cambricon', value: GPUDriverMap.CAMBRICON, runtime: 'cambricon', - driver: 'cnmon info' + driver: 'cnmon info', + gpuVendor: 'cambricon' }, [GPUDriverMap.METAX]: { label: 'Metax', value: GPUDriverMap.METAX, runtime: 'metax', - driver: 'mx-smi' + driver: 'mx-smi', + gpuVendor: 'metax' }, [GPUDriverMap.THEAD]: { label: 'T-Head PPU', value: GPUDriverMap.THEAD, runtime: '', // TODO: confirm runtime name - driver: 'ppu-smi' + driver: 'ppu-smi', + gpuVendor: 'thead' } }; diff --git a/src/request-config.tsx b/src/request-config.tsx index f880f9c9..46064bca 100644 --- a/src/request-config.tsx +++ b/src/request-config.tsx @@ -10,10 +10,6 @@ import { extraRequestInterceptors } from './request.extensions'; const NoBaseURLAPIs = ['/auth', '/v1', '/version', '/proxy', '/update']; export const requestConfig: RequestConfig = { - headers: { - 'Content-Security-Policy': "frame-ancestors 'self'", - 'X-Frame-Options': 'SAMEORIGIN' - }, errorConfig: { errorThrower: (res: any) => { // to do something