fix: style issues

This commit is contained in:
jialin
2026-05-11 19:49:21 +08:00
committed by jialin
parent 1b74285de9
commit 01fe2e66fb
33 changed files with 296 additions and 137 deletions
+1
View File
@@ -14,3 +14,4 @@
.DS_Store .DS_Store
.idea .idea
.claude .claude
/dist.zip
+1 -1
View File
@@ -36,5 +36,5 @@ export const getCurrentOrganizationId = (): string => {
if (value != null) return String(value); if (value != null) return String(value);
} }
} catch {} } catch {}
return 'gpustack-organization-1'; return 'gpustack-default';
}; };
+1 -1
View File
@@ -356,7 +356,7 @@ export default (props: any) => {
config={{ config={{
apiBaseUrl: GPUSTACK_API_BASE_URL, apiBaseUrl: GPUSTACK_API_BASE_URL,
theme: userSettings.theme, 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, isDarkTheme: userSettings.isDarkTheme,
defaultColorPrimary: COLOR_PRIMARY defaultColorPrimary: COLOR_PRIMARY
}} }}
+2 -1
View File
@@ -280,5 +280,6 @@ export default {
'common.file.size.limit': 'File size must not exceed {size}.', 'common.file.size.limit': 'File size must not exceed {size}.',
'common.file.format.limit': 'Invalid file format. Allowed: {formats}.', 'common.file.format.limit': 'Invalid file format. Allowed: {formats}.',
'common.image.limit.width': 'Image width must be {width}.', '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}'
}; };
+8 -5
View File
@@ -13,8 +13,8 @@ export default {
'gpuservice.template.command.placeholder': 'gpuservice.template.command.placeholder':
'Separate arguments with spaces; wrap arguments containing spaces in quotes, e.g.: /bin/bash -c "echo hello world"', '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.mountPath': 'Mount Path',
'gpuservice.template.containerDisk': 'Container Disk (Gi)', 'gpuservice.template.containerDisk': 'Container Disk (GB)',
'gpuservice.template.memory': 'Memory (Gi)', 'gpuservice.template.memory': 'Memory (GB)',
'gpuservice.template.ports': 'Ports', 'gpuservice.template.ports': 'Ports',
'gpuservice.template.ports.add': 'Add Port', 'gpuservice.template.ports.add': 'Add Port',
'gpuservice.template.ports.invalid': 'gpuservice.template.ports.invalid':
@@ -41,13 +41,16 @@ export default {
'gpuservice.instance.section.basic': 'Basic Info', 'gpuservice.instance.section.basic': 'Basic Info',
'gpuservice.instance.section.type': 'Instance Type', 'gpuservice.instance.section.type': 'Instance Type',
'gpuservice.instance.section.template': 'Instance Template', '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.type.required': 'Please select an instance type',
'gpuservice.instance.gpuCount': 'GPU Count', 'gpuservice.instance.gpuCount': 'GPU Count',
'gpuservice.instance.gpuCount.required': 'Please enter the GPU count', 'gpuservice.instance.gpuCount.required': 'Please enter the GPU count',
'gpuservice.instance.gpuCount.max': 'gpuservice.instance.gpuCount.max':
'The current instance type supports at most {count} GPU(s)', 'The current instance type supports at most {count} GPU(s)',
'gpuservice.instance.stock': 'Stock', 'gpuservice.instance.stock': 'Stock',
'gpuservice.instance.sliced': 'Sliced',
'gpuservice.instance.memory': 'Memory', 'gpuservice.instance.memory': 'Memory',
'gpuservice.instance.ram': 'RAM', 'gpuservice.instance.ram': 'RAM',
'gpuservice.instance.search.type.placeholder': 'gpuservice.instance.search.type.placeholder':
@@ -68,8 +71,8 @@ export default {
'gpuservice.storage.type.object': 'Object Storage', 'gpuservice.storage.type.object': 'Object Storage',
'gpuservice.storage.capacity': 'Capacity', 'gpuservice.storage.capacity': 'Capacity',
'gpuservice.storage.accessMode': 'Access Mode', 'gpuservice.storage.accessMode': 'Access Mode',
'gpuservice.storage.persistent': 'Persistent Storage', 'gpuservice.storage.persistent': 'Persistent',
'gpuservice.storage.temporary': 'Temporary Storage', 'gpuservice.storage.temporary': 'Temporary',
'gpuservice.storage.persistentVolume': 'Persistent Volume', 'gpuservice.storage.persistentVolume': 'Persistent Volume',
'gpuservice.storage.persistentVolume.required': 'gpuservice.storage.persistentVolume.required':
'Please select a persistent volume', 'Please select a persistent volume',
+3 -4
View File
@@ -55,15 +55,14 @@ export default {
'noresult.instances.nofound': 'No matching instances found.', 'noresult.instances.nofound': 'No matching instances found.',
'noresult.gpuservice.template.title': 'No Instance Templates', 'noresult.gpuservice.template.title': 'No Instance Templates',
'noresult.gpuservice.template.subTitle': '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': 'noresult.gpuservice.template.nofound':
'No matching instance templates found.', 'No matching instance templates found.',
'noresult.gpuservice.instance.title': 'No GPU Instances', 'noresult.gpuservice.instance.title': 'No GPU Instances',
'noresult.gpuservice.instance.subTitle': '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.instance.nofound': 'No matching GPU instances found.',
'noresult.gpuservice.storage.title': 'No Storage', 'noresult.gpuservice.storage.title': 'No Storage',
'noresult.gpuservice.storage.subTitle': 'noresult.gpuservice.storage.subTitle': 'No storage has been added yet.',
'Once you create a storage, it will appear here.',
'noresult.gpuservice.storage.nofound': 'No matching storage found.' 'noresult.gpuservice.storage.nofound': 'No matching storage found.'
}; };
+2 -1
View File
@@ -280,7 +280,8 @@ export default {
'common.file.size.limit': 'File size must not exceed {size}.', 'common.file.size.limit': 'File size must not exceed {size}.',
'common.file.format.limit': 'Invalid file format. Allowed: {formats}.', 'common.file.format.limit': 'Invalid file format. Allowed: {formats}.',
'common.image.limit.width': 'Image width must be {width}.', '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) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
+7 -4
View File
@@ -13,8 +13,8 @@ export default {
'gpuservice.template.command.placeholder': 'gpuservice.template.command.placeholder':
'引数はスペースで区切り、スペースを含む引数は引用符で囲んでください。例:/bin/bash -c "echo hello world"', '引数はスペースで区切り、スペースを含む引数は引用符で囲んでください。例:/bin/bash -c "echo hello world"',
'gpuservice.template.mountPath': 'マウントパス', 'gpuservice.template.mountPath': 'マウントパス',
'gpuservice.template.containerDisk': 'コンテナディスク (Gi)', 'gpuservice.template.containerDisk': 'コンテナディスク (GB)',
'gpuservice.template.memory': 'メモリ (Gi)', 'gpuservice.template.memory': 'メモリ (GB)',
'gpuservice.template.ports': 'ポート', 'gpuservice.template.ports': 'ポート',
'gpuservice.template.ports.add': 'ポートを追加', 'gpuservice.template.ports.add': 'ポートを追加',
'gpuservice.template.ports.invalid': 'ポート設定を完成させてください。', 'gpuservice.template.ports.invalid': 'ポート設定を完成させてください。',
@@ -39,6 +39,8 @@ export default {
'gpuservice.instance.section.basic': '基本情報', 'gpuservice.instance.section.basic': '基本情報',
'gpuservice.instance.section.type': 'インスタンスタイプ', 'gpuservice.instance.section.type': 'インスタンスタイプ',
'gpuservice.instance.section.template': 'インスタンステンプレート', 'gpuservice.instance.section.template': 'インスタンステンプレート',
'gpuservice.instance.types': 'インスタンスタイプ',
'gpuservice.instance.templates': 'インスタンステンプレート',
'gpuservice.instance.section.storage': 'ストレージボリューム', 'gpuservice.instance.section.storage': 'ストレージボリューム',
'gpuservice.instance.type.required': 'インスタンスタイプを選択してください', 'gpuservice.instance.type.required': 'インスタンスタイプを選択してください',
'gpuservice.instance.gpuCount': 'GPU 数', 'gpuservice.instance.gpuCount': 'GPU 数',
@@ -46,6 +48,7 @@ export default {
'gpuservice.instance.gpuCount.max': 'gpuservice.instance.gpuCount.max':
'現在のインスタンスタイプは最大 {count} 個の GPU をサポートします', '現在のインスタンスタイプは最大 {count} 個の GPU をサポートします',
'gpuservice.instance.stock': '在庫', 'gpuservice.instance.stock': '在庫',
'gpuservice.instance.sliced': '分割',
'gpuservice.instance.memory': 'Memory', 'gpuservice.instance.memory': 'Memory',
'gpuservice.instance.ram': 'RAM', 'gpuservice.instance.ram': 'RAM',
'gpuservice.instance.search.type.placeholder': 'gpuservice.instance.search.type.placeholder':
@@ -66,8 +69,8 @@ export default {
'gpuservice.storage.type.object': 'オブジェクトストレージ', 'gpuservice.storage.type.object': 'オブジェクトストレージ',
'gpuservice.storage.capacity': '容量', 'gpuservice.storage.capacity': '容量',
'gpuservice.storage.accessMode': 'アクセスモード', 'gpuservice.storage.accessMode': 'アクセスモード',
'gpuservice.storage.persistent': '永続ストレージ', 'gpuservice.storage.persistent': '永続',
'gpuservice.storage.temporary': '一時ストレージ', 'gpuservice.storage.temporary': '一時',
'gpuservice.storage.persistentVolume': '永続ボリューム', 'gpuservice.storage.persistentVolume': '永続ボリューム',
'gpuservice.storage.persistentVolume.required': 'gpuservice.storage.persistentVolume.required':
'永続ボリュームを選択してください', '永続ボリュームを選択してください',
+3 -3
View File
@@ -55,16 +55,16 @@ export default {
'noresult.instances.nofound': 'No matching instances found.', 'noresult.instances.nofound': 'No matching instances found.',
'noresult.gpuservice.template.title': 'インスタンステンプレートなし', 'noresult.gpuservice.template.title': 'インスタンステンプレートなし',
'noresult.gpuservice.template.subTitle': 'noresult.gpuservice.template.subTitle':
'インスタンステンプレートを作成すると、ここに表示されます。', 'インスタンステンプレートはまだ追加されていません。',
'noresult.gpuservice.template.nofound': 'noresult.gpuservice.template.nofound':
'一致するインスタンステンプレートが見つかりません。', '一致するインスタンステンプレートが見つかりません。',
'noresult.gpuservice.instance.title': 'GPU インスタンスなし', 'noresult.gpuservice.instance.title': 'GPU インスタンスなし',
'noresult.gpuservice.instance.subTitle': 'noresult.gpuservice.instance.subTitle':
'GPU インスタンスを作成すると、ここに表示されます。', 'GPU インスタンスはまだ追加されていません。',
'noresult.gpuservice.instance.nofound': 'noresult.gpuservice.instance.nofound':
'一致する GPU インスタンスが見つかりません。', '一致する GPU インスタンスが見つかりません。',
'noresult.gpuservice.storage.title': 'ストレージなし', 'noresult.gpuservice.storage.title': 'ストレージなし',
'noresult.gpuservice.storage.subTitle': 'noresult.gpuservice.storage.subTitle':
'ストレージを作成すると、ここに表示されます。', 'ストレージはまだ追加されていません。',
'noresult.gpuservice.storage.nofound': '一致するストレージが見つかりません。' 'noresult.gpuservice.storage.nofound': '一致するストレージが見つかりません。'
}; };
+2 -1
View File
@@ -279,7 +279,8 @@ export default {
'common.file.size.limit': 'File size must not exceed {size}.', 'common.file.size.limit': 'File size must not exceed {size}.',
'common.file.format.limit': 'Invalid file format. Allowed: {formats}.', 'common.file.format.limit': 'Invalid file format. Allowed: {formats}.',
'common.image.limit.width': 'Image width must be {width}.', '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) ========== // ========== To-Do: Translate Keys (Remove After Translation) ==========
+7 -4
View File
@@ -14,8 +14,8 @@ export default {
'gpuservice.template.command.placeholder': 'gpuservice.template.command.placeholder':
'Разделяйте аргументы пробелами; аргументы с пробелами заключайте в кавычки, например: /bin/bash -c "echo hello world"', 'Разделяйте аргументы пробелами; аргументы с пробелами заключайте в кавычки, например: /bin/bash -c "echo hello world"',
'gpuservice.template.mountPath': 'Путь монтирования', 'gpuservice.template.mountPath': 'Путь монтирования',
'gpuservice.template.containerDisk': 'Диск контейнера (Gi)', 'gpuservice.template.containerDisk': 'Диск контейнера (GB)',
'gpuservice.template.memory': 'Память (Gi)', 'gpuservice.template.memory': 'Память (GB)',
'gpuservice.template.ports': 'Порты', 'gpuservice.template.ports': 'Порты',
'gpuservice.template.ports.add': 'Добавить порт', 'gpuservice.template.ports.add': 'Добавить порт',
'gpuservice.template.ports.invalid': 'Заполните настройки портов полностью.', 'gpuservice.template.ports.invalid': 'Заполните настройки портов полностью.',
@@ -41,6 +41,8 @@ export default {
'gpuservice.instance.section.basic': 'Основная информация', 'gpuservice.instance.section.basic': 'Основная информация',
'gpuservice.instance.section.type': 'Тип экземпляра', 'gpuservice.instance.section.type': 'Тип экземпляра',
'gpuservice.instance.section.template': 'Шаблон экземпляра', 'gpuservice.instance.section.template': 'Шаблон экземпляра',
'gpuservice.instance.types': 'Типы экземпляров',
'gpuservice.instance.templates': 'Шаблоны экземпляров',
'gpuservice.instance.section.storage': 'Том хранилища', 'gpuservice.instance.section.storage': 'Том хранилища',
'gpuservice.instance.type.required': 'Выберите тип экземпляра', 'gpuservice.instance.type.required': 'Выберите тип экземпляра',
'gpuservice.instance.gpuCount': 'Количество GPU', 'gpuservice.instance.gpuCount': 'Количество GPU',
@@ -48,6 +50,7 @@ export default {
'gpuservice.instance.gpuCount.max': 'gpuservice.instance.gpuCount.max':
'Текущий тип экземпляра поддерживает максимум {count} GPU', 'Текущий тип экземпляра поддерживает максимум {count} GPU',
'gpuservice.instance.stock': 'Остаток', 'gpuservice.instance.stock': 'Остаток',
'gpuservice.instance.sliced': 'Разделено',
'gpuservice.instance.memory': 'Память', 'gpuservice.instance.memory': 'Память',
'gpuservice.instance.ram': 'RAM', 'gpuservice.instance.ram': 'RAM',
'gpuservice.instance.search.type.placeholder': 'gpuservice.instance.search.type.placeholder':
@@ -68,8 +71,8 @@ export default {
'gpuservice.storage.type.object': 'Объектное хранилище', 'gpuservice.storage.type.object': 'Объектное хранилище',
'gpuservice.storage.capacity': 'Объём', 'gpuservice.storage.capacity': 'Объём',
'gpuservice.storage.accessMode': 'Режим доступа', 'gpuservice.storage.accessMode': 'Режим доступа',
'gpuservice.storage.persistent': 'Постоянное хранилище', 'gpuservice.storage.persistent': 'Постоянное',
'gpuservice.storage.temporary': 'Временное хранилище', 'gpuservice.storage.temporary': 'Временное',
'gpuservice.storage.persistentVolume': 'Постоянный том', 'gpuservice.storage.persistentVolume': 'Постоянный том',
'gpuservice.storage.persistentVolume.required': 'Выберите постоянный том', 'gpuservice.storage.persistentVolume.required': 'Выберите постоянный том',
'gpuservice.storage.tempCapacity': 'Объём хранилища (ГБ)', 'gpuservice.storage.tempCapacity': 'Объём хранилища (ГБ)',
+3 -5
View File
@@ -56,17 +56,15 @@ export default {
'noresult.instances.nofound': 'No matching instances found.', 'noresult.instances.nofound': 'No matching instances found.',
'noresult.gpuservice.template.title': 'Нет шаблонов экземпляров', 'noresult.gpuservice.template.title': 'Нет шаблонов экземпляров',
'noresult.gpuservice.template.subTitle': 'noresult.gpuservice.template.subTitle':
'После создания шаблона экземпляра он появится здесь.', 'Шаблоны экземпляров ещё не добавлены.',
'noresult.gpuservice.template.nofound': 'noresult.gpuservice.template.nofound':
'Подходящие шаблоны экземпляров не найдены.', 'Подходящие шаблоны экземпляров не найдены.',
'noresult.gpuservice.instance.title': 'Нет экземпляров GPU', 'noresult.gpuservice.instance.title': 'Нет экземпляров GPU',
'noresult.gpuservice.instance.subTitle': 'noresult.gpuservice.instance.subTitle': 'Экземпляры GPU ещё не добавлены.',
'После создания экземпляра GPU он появится здесь.',
'noresult.gpuservice.instance.nofound': 'noresult.gpuservice.instance.nofound':
'Подходящие экземпляры GPU не найдены.', 'Подходящие экземпляры GPU не найдены.',
'noresult.gpuservice.storage.title': 'Нет хранилищ', 'noresult.gpuservice.storage.title': 'Нет хранилищ',
'noresult.gpuservice.storage.subTitle': 'noresult.gpuservice.storage.subTitle': 'Хранилища ещё не добавлены.',
'После создания хранилища оно появится здесь.',
'noresult.gpuservice.storage.nofound': 'Подходящие хранилища не найдены.' 'noresult.gpuservice.storage.nofound': 'Подходящие хранилища не найдены.'
}; };
+2 -1
View File
@@ -283,5 +283,6 @@ export default {
'common.file.size.limit': 'File size must not exceed {size}.', 'common.file.size.limit': 'File size must not exceed {size}.',
'common.file.format.limit': 'Invalid file format. Allowed: {formats}.', 'common.file.format.limit': 'Invalid file format. Allowed: {formats}.',
'common.image.limit.width': 'Image width must be {width}.', '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}'
}; };
+7 -4
View File
@@ -13,8 +13,8 @@ export default {
'gpuservice.template.command.placeholder': '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"', '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.mountPath': 'Bağlama Yolu',
'gpuservice.template.containerDisk': 'Konteyner Diski (Gi)', 'gpuservice.template.containerDisk': 'Konteyner Diski (GB)',
'gpuservice.template.memory': 'Bellek (Gi)', 'gpuservice.template.memory': 'Bellek (GB)',
'gpuservice.template.ports': 'Bağlantı Noktaları', 'gpuservice.template.ports': 'Bağlantı Noktaları',
'gpuservice.template.ports.add': 'Bağlantı Noktası Ekle', 'gpuservice.template.ports.add': 'Bağlantı Noktası Ekle',
'gpuservice.template.ports.invalid': 'gpuservice.template.ports.invalid':
@@ -40,6 +40,8 @@ export default {
'gpuservice.instance.section.basic': 'Temel Bilgiler', 'gpuservice.instance.section.basic': 'Temel Bilgiler',
'gpuservice.instance.section.type': 'Örnek Türü', 'gpuservice.instance.section.type': 'Örnek Türü',
'gpuservice.instance.section.template': 'Örnek Şablonu', '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.section.storage': 'Depolama Hacmi',
'gpuservice.instance.type.required': 'Lütfen bir örnek türü seçin', 'gpuservice.instance.type.required': 'Lütfen bir örnek türü seçin',
'gpuservice.instance.gpuCount': 'GPU Sayısı', 'gpuservice.instance.gpuCount': 'GPU Sayısı',
@@ -47,6 +49,7 @@ export default {
'gpuservice.instance.gpuCount.max': 'gpuservice.instance.gpuCount.max':
'Mevcut örnek türü en fazla {count} GPU destekler', 'Mevcut örnek türü en fazla {count} GPU destekler',
'gpuservice.instance.stock': 'Stok', 'gpuservice.instance.stock': 'Stok',
'gpuservice.instance.sliced': 'Bölünmüş',
'gpuservice.instance.memory': 'Bellek', 'gpuservice.instance.memory': 'Bellek',
'gpuservice.instance.ram': 'RAM', 'gpuservice.instance.ram': 'RAM',
'gpuservice.instance.search.type.placeholder': 'gpuservice.instance.search.type.placeholder':
@@ -67,8 +70,8 @@ export default {
'gpuservice.storage.type.object': 'Nesne Depolama', 'gpuservice.storage.type.object': 'Nesne Depolama',
'gpuservice.storage.capacity': 'Kapasite', 'gpuservice.storage.capacity': 'Kapasite',
'gpuservice.storage.accessMode': 'Erişim Modu', 'gpuservice.storage.accessMode': 'Erişim Modu',
'gpuservice.storage.persistent': 'Kalıcı Depolama', 'gpuservice.storage.persistent': 'Kalıcı',
'gpuservice.storage.temporary': 'Geçici Depolama', 'gpuservice.storage.temporary': 'Geçici',
'gpuservice.storage.persistentVolume': 'Kalıcı Hacim', 'gpuservice.storage.persistentVolume': 'Kalıcı Hacim',
'gpuservice.storage.persistentVolume.required': 'gpuservice.storage.persistentVolume.required':
'Lütfen bir kalıcı hacim seçin', 'Lütfen bir kalıcı hacim seçin',
+3 -6
View File
@@ -56,16 +56,13 @@ export default {
'There are no running model instances. Deploy a model to create one automatically.', 'There are no running model instances. Deploy a model to create one automatically.',
'noresult.instances.nofound': 'No matching instances found.', 'noresult.instances.nofound': 'No matching instances found.',
'noresult.gpuservice.template.title': 'Örnek Şablonu Yok', 'noresult.gpuservice.template.title': 'Örnek Şablonu Yok',
'noresult.gpuservice.template.subTitle': 'noresult.gpuservice.template.subTitle': 'Henüz örnek şablonu eklenmedi.',
'Bir örnek şablonu oluşturduğunuzda burada görünecek.',
'noresult.gpuservice.template.nofound': 'Eşleşen örnek şablonu bulunamadı.', 'noresult.gpuservice.template.nofound': 'Eşleşen örnek şablonu bulunamadı.',
'noresult.gpuservice.instance.title': 'GPU Örneği Yok', 'noresult.gpuservice.instance.title': 'GPU Örneği Yok',
'noresult.gpuservice.instance.subTitle': 'noresult.gpuservice.instance.subTitle': 'Henüz GPU örneği eklenmedi.',
'Bir GPU örneği oluşturduğunuzda burada görünecek.',
'noresult.gpuservice.instance.nofound': 'Eşleşen GPU örneği bulunamadı.', 'noresult.gpuservice.instance.nofound': 'Eşleşen GPU örneği bulunamadı.',
'noresult.gpuservice.storage.title': 'Depolama Yok', 'noresult.gpuservice.storage.title': 'Depolama Yok',
'noresult.gpuservice.storage.subTitle': 'noresult.gpuservice.storage.subTitle': 'Henüz depolama eklenmedi.',
'Bir depolama oluşturduğunuzda burada görünecek.',
'noresult.gpuservice.storage.nofound': 'Eşleşen depolama bulunamadı.' 'noresult.gpuservice.storage.nofound': 'Eşleşen depolama bulunamadı.'
}; };
+2 -1
View File
@@ -272,5 +272,6 @@ export default {
'common.file.size.limit': '文件大小不超过{size}。', 'common.file.size.limit': '文件大小不超过{size}。',
'common.file.format.limit': '文件格式不正确,仅支持{formats}。', 'common.file.format.limit': '文件格式不正确,仅支持{formats}。',
'common.image.limit.width': '图片宽度须为{width}。', 'common.image.limit.width': '图片宽度须为{width}。',
'common.image.limit.height': '图片高度须为{height}。' 'common.image.limit.height': '图片高度须为{height}。',
'common.max': '最大 {count}'
}; };
+8 -5
View File
@@ -13,8 +13,8 @@ export default {
'gpuservice.template.command.placeholder': 'gpuservice.template.command.placeholder':
'使用空格分隔参数;含空格的参数请用引号包裹,例如:/bin/bash -c "echo hello world"', '使用空格分隔参数;含空格的参数请用引号包裹,例如:/bin/bash -c "echo hello world"',
'gpuservice.template.mountPath': '挂载路径', 'gpuservice.template.mountPath': '挂载路径',
'gpuservice.template.containerDisk': '容器磁盘(Gi)', 'gpuservice.template.containerDisk': '容器磁盘(GB)',
'gpuservice.template.memory': '内存(Gi)', 'gpuservice.template.memory': '内存(GB)',
'gpuservice.template.ports': '端口', 'gpuservice.template.ports': '端口',
'gpuservice.template.ports.add': '添加端口', 'gpuservice.template.ports.add': '添加端口',
'gpuservice.template.ports.invalid': '请填写完整的端口配置', 'gpuservice.template.ports.invalid': '请填写完整的端口配置',
@@ -29,7 +29,7 @@ export default {
'gpuservice.template.card.ports': '端口', 'gpuservice.template.card.ports': '端口',
'gpuservice.publicKey.label': 'SSH 公钥', 'gpuservice.publicKey.label': 'SSH 公钥',
'gpuservice.publicKey.placeholder': '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': 'GPU 实例',
'gpuservice.instance.add': '添加 GPU 实例', 'gpuservice.instance.add': '添加 GPU 实例',
'gpuservice.instance.edit': '编辑 GPU 实例', 'gpuservice.instance.edit': '编辑 GPU 实例',
@@ -39,12 +39,15 @@ export default {
'gpuservice.instance.section.basic': '基础信息', 'gpuservice.instance.section.basic': '基础信息',
'gpuservice.instance.section.type': '实例类型', 'gpuservice.instance.section.type': '实例类型',
'gpuservice.instance.section.template': '实例模板', 'gpuservice.instance.section.template': '实例模板',
'gpuservice.instance.types': '实例类型',
'gpuservice.instance.templates': '实例模板',
'gpuservice.instance.section.storage': '存储卷', 'gpuservice.instance.section.storage': '存储卷',
'gpuservice.instance.type.required': '请选择实例类型', 'gpuservice.instance.type.required': '请选择实例类型',
'gpuservice.instance.gpuCount': 'GPU 数量', 'gpuservice.instance.gpuCount': 'GPU 数量',
'gpuservice.instance.gpuCount.required': '请输入 GPU 数量', 'gpuservice.instance.gpuCount.required': '请输入 GPU 数量',
'gpuservice.instance.gpuCount.max': '当前实例类型最多支持 {count} 个 GPU', 'gpuservice.instance.gpuCount.max': '当前实例类型最多支持 {count} 个 GPU',
'gpuservice.instance.stock': '库存', 'gpuservice.instance.stock': '库存',
'gpuservice.instance.sliced': '切分',
'gpuservice.instance.memory': '显存', 'gpuservice.instance.memory': '显存',
'gpuservice.instance.ram': '内存', 'gpuservice.instance.ram': '内存',
'gpuservice.instance.search.type.placeholder': '搜索名称、显存、内存或 vCPU', 'gpuservice.instance.search.type.placeholder': '搜索名称、显存、内存或 vCPU',
@@ -64,8 +67,8 @@ export default {
'gpuservice.storage.type.object': '对象存储', 'gpuservice.storage.type.object': '对象存储',
'gpuservice.storage.capacity': '容量', 'gpuservice.storage.capacity': '容量',
'gpuservice.storage.accessMode': '访问模式', 'gpuservice.storage.accessMode': '访问模式',
'gpuservice.storage.persistent': '持久存储', 'gpuservice.storage.persistent': '持久',
'gpuservice.storage.temporary': '临时存储', 'gpuservice.storage.temporary': '临时',
'gpuservice.storage.persistentVolume': '持久卷', 'gpuservice.storage.persistentVolume': '持久卷',
'gpuservice.storage.persistentVolume.required': '请选择持久卷', 'gpuservice.storage.persistentVolume.required': '请选择持久卷',
'gpuservice.storage.tempCapacity': '存储容量 (GB)', 'gpuservice.storage.tempCapacity': '存储容量 (GB)',
+3 -3
View File
@@ -52,12 +52,12 @@ export default {
'当前没有运行中的模型实例,部署模型后将自动创建。', '当前没有运行中的模型实例,部署模型后将自动创建。',
'noresult.instances.nofound': '未找到匹配的实例', 'noresult.instances.nofound': '未找到匹配的实例',
'noresult.gpuservice.template.title': '暂无实例模板', 'noresult.gpuservice.template.title': '暂无实例模板',
'noresult.gpuservice.template.subTitle': '创建一个实例模板后会显示在这里', 'noresult.gpuservice.template.subTitle': '尚未添加任何实例模板。',
'noresult.gpuservice.template.nofound': '未找到匹配的实例模板', 'noresult.gpuservice.template.nofound': '未找到匹配的实例模板',
'noresult.gpuservice.instance.title': '暂无 GPU 实例', 'noresult.gpuservice.instance.title': '暂无 GPU 实例',
'noresult.gpuservice.instance.subTitle': '创建一个 GPU 实例后会显示在这里', 'noresult.gpuservice.instance.subTitle': '尚未添加任何 GPU 实例',
'noresult.gpuservice.instance.nofound': '未找到匹配的 GPU 实例', 'noresult.gpuservice.instance.nofound': '未找到匹配的 GPU 实例',
'noresult.gpuservice.storage.title': '暂无存储', 'noresult.gpuservice.storage.title': '暂无存储',
'noresult.gpuservice.storage.subTitle': '创建一个存储后会显示在这里', 'noresult.gpuservice.storage.subTitle': '尚未添加任何存储。',
'noresult.gpuservice.storage.nofound': '未找到匹配的存储' 'noresult.gpuservice.storage.nofound': '未找到匹配的存储'
}; };
@@ -24,7 +24,7 @@ const Container = styled.div`
const ColWrapper = styled.div` const ColWrapper = styled.div`
display: flex; display: flex;
flex: 1; flex: 1;
max-width: 33.33%; max-width: 33%;
min-height: 0; min-height: 0;
`; `;
@@ -39,7 +39,7 @@ const PanelBody = styled.div`
const FormWrapper = styled.div` const FormWrapper = styled.div`
display: flex; display: flex;
flex: 1; flex: 1;
max-width: 33.33%; max-width: 34%;
min-height: 0; min-height: 0;
`; `;
@@ -52,6 +52,30 @@ type AddModalProps = {
onCancel: () => void; onCancel: () => void;
}; };
const ColTitle: React.FC<{
children: React.ReactNode;
style?: React.CSSProperties;
}> = ({ children, style }) => {
return (
<Typography.Title
level={3}
style={{
fontSize: 14,
paddingTop: 10,
paddingBottom: 16,
margin: 0,
position: 'sticky',
top: 0,
zIndex: 100,
backgroundColor: 'var(--ant-color-bg-elevated)',
...style
}}
>
{children}
</Typography.Title>
);
};
const AddModal: React.FC<AddModalProps> = ({ const AddModal: React.FC<AddModalProps> = ({
title, title,
action, action,
@@ -81,7 +105,7 @@ const AddModal: React.FC<AddModalProps> = ({
if (open) { if (open) {
autoSelectedRef.current = false; autoSelectedRef.current = false;
fetchData({}); fetchData({});
fetchTemplates({ page: 1, perPage: 100 }); fetchTemplates({ page: -1 });
} else { } else {
setSelectedInstanceType(undefined); setSelectedInstanceType(undefined);
setSelectedManufacturer(undefined); setSelectedManufacturer(undefined);
@@ -112,6 +136,11 @@ const AddModal: React.FC<AddModalProps> = ({
}, [instanceKeyword, instanceTypeList]); }, [instanceKeyword, instanceTypeList]);
const manufacturerMatchedTemplates = useMemo(() => { const manufacturerMatchedTemplates = useMemo(() => {
console.log(
'Filtering templates by manufacturer:',
templateList,
selectedManufacturer
);
if (!selectedManufacturer) { if (!selectedManufacturer) {
return templateList; return templateList;
} }
@@ -277,6 +306,9 @@ const AddModal: React.FC<AddModalProps> = ({
<ColWrapper> <ColWrapper>
<ColumnWrapper styles={{ container: { paddingBlock: 0 } }}> <ColumnWrapper styles={{ container: { paddingBlock: 0 } }}>
<PanelBody> <PanelBody>
<ColTitle style={{ paddingBottom: 0 }}>
{intl.formatMessage({ id: 'gpuservice.instance.types' })}
</ColTitle>
<div <div
style={{ style={{
position: 'sticky', position: 'sticky',
@@ -308,6 +340,9 @@ const AddModal: React.FC<AddModalProps> = ({
<ColWrapper> <ColWrapper>
<ColumnWrapper styles={{ container: { paddingBlock: 0 } }}> <ColumnWrapper styles={{ container: { paddingBlock: 0 } }}>
<PanelBody> <PanelBody>
<ColTitle style={{ paddingBottom: 0 }}>
{intl.formatMessage({ id: 'gpuservice.instance.templates' })}
</ColTitle>
<div <div
style={{ style={{
position: 'sticky', position: 'sticky',
@@ -355,27 +390,16 @@ const AddModal: React.FC<AddModalProps> = ({
} }
> >
<> <>
<Typography.Title <ColTitle>
level={3} {intl.formatMessage({ id: 'common.title.config' })}
style={{ </ColTitle>
fontSize: 14,
paddingTop: 10,
paddingBottom: 16,
margin: 0,
position: 'sticky',
top: 0,
zIndex: 100,
backgroundColor: 'var(--ant-color-bg-elevated)'
}}
>
Configuration
</Typography.Title>
<GPUServiceInstanceForm <GPUServiceInstanceForm
ref={form} ref={form}
action={action} action={action}
currentData={data} currentData={data}
onFinish={onFinish} onFinish={onFinish}
open={open} open={open}
instanceTypeList={instanceTypeList}
/> />
</> </>
</ColumnWrapper> </ColumnWrapper>
@@ -115,16 +115,38 @@ const InstanceTypeItem: React.FC<InstanceTypeItemProps> = ({
<span>{convertKiToGi(item.spec?.memory) ?? '-'}</span> <span>{convertKiToGi(item.spec?.memory) ?? '-'}</span>
</Flex> </Flex>
</span> </span>
<span className="dot"></span> {item.spec?.sliced && (
<span className="meta-item"> <>
<IconFont type="icon-cube" className="meta-icon" /> <span className="dot"></span>
<Flex align="center" gap={4}> <span className="meta-item">
<span> <IconFont type="icon-sliced" className="meta-icon" />
{intl.formatMessage({ id: 'gpuservice.instance.stock' })} <Flex align="center" gap={4}>
<span>
{intl.formatMessage({
id: 'gpuservice.instance.sliced'
})}
</span>
<span>{item.spec?.sliced ?? '-'}</span>
</Flex>
</span> </span>
<span>{item.status?.accelerator?.remaining ?? '-'}</span> </>
</Flex> )}
</span> {item.status?.accelerator?.remaining && (
<>
<span className="dot"></span>
<span className="meta-item">
<IconFont type="icon-cube" className="meta-icon" />
<Flex align="center" gap={4}>
<span>
{intl.formatMessage({
id: 'gpuservice.instance.stock'
})}
</span>
<span>{item.status?.accelerator?.remaining ?? '-'}</span>
</Flex>
</span>
</>
)}
</span> </span>
)} )}
</span> </span>
@@ -130,6 +130,10 @@ export const transformInstanceType = (
const status = item.status; const status = item.status;
return { return {
...item, ...item,
spec: {
...item.spec,
manufacturer: !item.spec?.acceleratable ? 'cpu' : item.spec.manufacturer
},
status: { status: {
...status, ...status,
accelerator: transformInstanceTypeResource( accelerator: transformInstanceTypeResource(
@@ -20,8 +20,10 @@ import {
} from 'react'; } from 'react';
import useGetSshkey from '../../public-keys/services/use-get-sshkey'; import useGetSshkey from '../../public-keys/services/use-get-sshkey';
import { DefaultImagePullPolicy } from '../../templates/config'; import { DefaultImagePullPolicy } from '../../templates/config';
import TemplateBasicForm from '../../templates/forms/basic'; import TemplateBasicForm, {
import { FormData, InstancePort, ListItem } from '../config/types'; BasicResourceMax
} from '../../templates/forms/basic';
import { FormData, InstanceTypeItem, ListItem } from '../config/types';
import Basic from './basic'; import Basic from './basic';
import InstanceTypeFormItem from './instance-type'; import InstanceTypeFormItem from './instance-type';
import StorageVolume from './storage-volume'; import StorageVolume from './storage-volume';
@@ -39,9 +41,18 @@ interface InstanceFormProps {
action: PageActionType; action: PageActionType;
currentData?: ListItem | null; currentData?: ListItem | null;
namespace?: string; namespace?: string;
instanceTypeList?: InstanceTypeItem[];
onFinish: (values: FormData) => Promise<void>; onFinish: (values: FormData) => Promise<void>;
} }
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 = { const TABKeysMap = {
BASIC: 'basic', BASIC: 'basic',
INSTANCE_TYPE: 'instanceType', INSTANCE_TYPE: 'instanceType',
@@ -75,6 +86,7 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
currentData, currentData,
open, open,
namespace = 'default', namespace = 'default',
instanceTypeList = [],
onFinish onFinish
} = props; } = props;
const intl = useIntl(); const intl = useIntl();
@@ -108,7 +120,7 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
{ {
value: TABKeysMap.BASIC, value: TABKeysMap.BASIC,
label: intl.formatMessage({ label: intl.formatMessage({
id: 'gpuservice.instance.section.basic' id: 'common.title.basicInfo'
}), }),
icon: <IconFont type="icon-basic" />, icon: <IconFont type="icon-basic" />,
field: 'name' field: 'name'
@@ -139,14 +151,31 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
[intl] [intl]
); );
const ports = (Form.useWatch(['spec', 'ports'], form) || const ports = Form.useWatch(['spec', 'ports'], form) || [];
[]) as InstancePort[];
const hasSshPort = useMemo( const hasSshPort = useMemo(
() => () =>
ports.some((p) => Number(p?.port) === SSH_PORT && p.protocol === 'TCP'), ports.some((p) => Number(p?.port) === SSH_PORT && p.protocol === 'TCP'),
[ports] [ports]
); );
const selectedTypeName = Form.useWatch(['spec', 'type'], form) as
| string
| undefined;
const onceMaxRequest = useMemo<BasicResourceMax>(() => {
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) => { const onTargetChange = (key: string) => {
scrollTabsRef.current?.handleTargetChange(key); scrollTabsRef.current?.handleTargetChange(key);
}; };
@@ -277,7 +306,12 @@ const GPUServiceInstanceForm: React.FC<InstanceFormProps> = forwardRef(
id: 'gpuservice.instance.section.template' id: 'gpuservice.instance.section.template'
}), }),
forceRender: true, forceRender: true,
children: <TemplateBasicForm page="instance" /> children: (
<TemplateBasicForm
page="instance"
onceMaxRequest={onceMaxRequest}
/>
)
}, },
{ {
key: TABKeysMap.STORAGE, key: TABKeysMap.STORAGE,
@@ -1,6 +1,6 @@
import { InputNumber as CInputNumber } from '@gpustack/core-ui'; import { InputNumber as CInputNumber } from '@gpustack/core-ui';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Form } from 'antd'; import { Flex, Form } from 'antd';
import { useEffect, useMemo } from 'react'; import { useEffect, useMemo } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import InstanceTypeItem from '../components/instance-type-item'; import InstanceTypeItem from '../components/instance-type-item';
@@ -138,7 +138,19 @@ const InstanceTypeFormItem = () => {
min={1} min={1}
max={maxGpuCount} max={maxGpuCount}
precision={0} precision={0}
label={intl.formatMessage({ id: 'gpuservice.instance.gpuCount' })} label={
<Flex gap={4} align="center">
{intl.formatMessage({ id: 'gpuservice.instance.gpuCount' })}
<span>
(
{intl.formatMessage(
{ id: 'common.max' },
{ count: maxGpuCount }
)}
)
</span>
</Flex>
}
required required
/> />
</Form.Item> </Form.Item>
@@ -33,7 +33,7 @@ const TemplateContent = styled.div`
} }
.value { .value {
color: var(--ant-color-text); color: var(--ant-color-text-secondary);
} }
.icon { .icon {
@@ -134,7 +134,7 @@ const useInstancesColumns = ({
backgroundColor: 'var(--ant-color-fill-tertiary)' backgroundColor: 'var(--ant-color-fill-tertiary)'
}} }}
> >
{entry.port} <span>{entry.port}</span>
<ExportOutlined style={{ fontSize: 10, marginLeft: 4 }} /> <ExportOutlined style={{ fontSize: 10, marginLeft: 4 }} />
</Button> </Button>
</Flex> </Flex>
+4 -1
View File
@@ -116,7 +116,10 @@ export async function queryStorageClass(
{ {
method: 'GET', method: 'GET',
params: omitPathParams(params), params: omitPathParams(params),
cancelToken: options?.token cancelToken: options?.token,
headers: {
skipErrorHandler: 'true'
}
} }
); );
} }
+3 -12
View File
@@ -28,10 +28,7 @@ const Basic = ({ open }: { open: boolean }) => {
rules={[ rules={[
{ {
required: true, required: true,
message: getRuleMessage( message: getRuleMessage('input', 'common.table.name')
'input',
intl.formatMessage({ id: 'common.table.name' })
)
} }
]} ]}
> >
@@ -47,10 +44,7 @@ const Basic = ({ open }: { open: boolean }) => {
rules={[ rules={[
{ {
required: true, required: true,
message: getRuleMessage( message: getRuleMessage('select', 'gpuservice.storage.type')
'select',
intl.formatMessage({ id: 'gpuservice.storage.type' })
)
} }
]} ]}
> >
@@ -72,10 +66,7 @@ const Basic = ({ open }: { open: boolean }) => {
rules={[ rules={[
{ {
required: true, required: true,
message: getRuleMessage( message: getRuleMessage('input', 'gpuservice.storage.capacity')
'input',
intl.formatMessage({ id: 'gpuservice.storage.capacity' })
)
} }
]} ]}
> >
@@ -52,7 +52,7 @@ const useStorageColumns = ({
dataIndex: ['spec', 'capacity'], dataIndex: ['spec', 'capacity'],
key: 'capacity', key: 'capacity',
sorter: false, sorter: false,
render: (value: string) => value ?? '-' render: (value: string) => (value ? value.replace(/Gi$/, 'GB') : '-')
}, },
{ {
title: intl.formatMessage({ id: 'common.table.status' }), title: intl.formatMessage({ id: 'common.table.status' }),
@@ -21,8 +21,8 @@ export default function useQueryStorageClass() {
>({ >({
key: 'storageClass', key: 'storageClass',
fetchList, fetchList,
getLabel: (item) => item.metadata.name, getLabel: (item) => item?.metadata.name,
getValue: (item) => item.metadata.name getValue: (item) => item?.metadata.name
}); });
return { return {
@@ -7,7 +7,7 @@ import mooreLogo from '@/assets/logo/moore-logo.png';
import nvidiaLogo from '@/assets/logo/nvidia.png'; import nvidiaLogo from '@/assets/logo/nvidia.png';
import theadLogoEN from '@/assets/logo/t-head-en.png'; import theadLogoEN from '@/assets/logo/t-head-en.png';
import theadLogoZH from '@/assets/logo/t-head-zh.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 { import {
AutoTooltip, AutoTooltip,
DropdownActions, DropdownActions,
@@ -73,7 +73,7 @@ const InfoItem = styled.div`
color: var(--ant-color-text-quaternary); color: var(--ant-color-text-quaternary);
} }
.value { .value {
color: var(--ant-color-text); color: var(--ant-color-text-secondary);
} }
`; `;
@@ -99,28 +99,28 @@ const TemplateCardItem: React.FC<TemplateCardProps> = ({ data, onSelect }) => {
const renderLogo = () => { const renderLogo = () => {
switch (data.manufacturer) { switch (data.manufacturer) {
case GPUDriverMap.NVIDIA: case manfacturerValueMap.NVIDIA:
return <LogoImg src={nvidiaLogo} height={18} />; return <LogoImg src={nvidiaLogo} height={18} />;
case GPUDriverMap.AMD: case manfacturerValueMap.AMD:
return ( return (
<IconFont <IconFont
type="icon-amd-logo" type="icon-amd-logo"
style={{ fontSize: 36, color: 'var(--ant-color-text)' }} style={{ fontSize: 36, color: 'var(--ant-color-text)' }}
/> />
); );
case GPUDriverMap.ASCEND: case manfacturerValueMap.ASCEND:
return <LogoImg src={ascendLogo} height={24} />; return <LogoImg src={ascendLogo} height={24} />;
case GPUDriverMap.HYGON: case manfacturerValueMap.HYGON:
return <LogoImg src={hyponPNG} height={18} />; return <LogoImg src={hyponPNG} height={18} />;
case GPUDriverMap.METAX: case manfacturerValueMap.METAX:
return <LogoImg src={metaxLogo} height={20} />; return <LogoImg src={metaxLogo} height={20} />;
case GPUDriverMap.MOORE_THREADS: case manfacturerValueMap.MOORE_THREADS:
return <LogoImg src={mooreLogo} height={22} />; return <LogoImg src={mooreLogo} height={22} />;
case GPUDriverMap.ILUVATAR: case manfacturerValueMap.ILUVATAR:
return <LogoImg src={iluvatarWEBP} height={22} />; return <LogoImg src={iluvatarWEBP} height={22} />;
case GPUDriverMap.CAMBRICON: case manfacturerValueMap.CAMBRICON:
return <LogoImg src={CambriconPNG} height={22} />; return <LogoImg src={CambriconPNG} height={22} />;
case GPUDriverMap.THEAD: case manfacturerValueMap.THEAD:
return ( return (
<LogoImg <LogoImg
src={intl?.locale === 'zh-CN' ? theadLogoZH : theadLogoEN} src={intl?.locale === 'zh-CN' ? theadLogoZH : theadLogoEN}
@@ -18,11 +18,18 @@ import { FormData } from '../config/types';
import Env from './env'; import Env from './env';
import Ports from './ports'; import Ports from './ports';
interface BasicProps { export interface BasicResourceMax {
page?: 'template' | 'instance'; cpu?: number | null;
memory?: number | null;
localStorage?: number | null;
} }
const Basic: React.FC<BasicProps> = ({ page = 'template' }) => { interface BasicProps {
page?: 'template' | 'instance';
onceMaxRequest?: BasicResourceMax;
}
const Basic: React.FC<BasicProps> = ({ page = 'template', onceMaxRequest }) => {
const { getRuleMessage } = useAppUtils(); const { getRuleMessage } = useAppUtils();
const intl = useIntl(); const intl = useIntl();
@@ -30,11 +37,26 @@ const Basic: React.FC<BasicProps> = ({ page = 'template' }) => {
() => () =>
Object.values(GPUsConfigs).map((item) => ({ Object.values(GPUsConfigs).map((item) => ({
label: item.label, 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 (
<Flex gap={4} align="center">
{label}
<span>
({intl.formatMessage({ id: 'common.max' }, { count: max })})
</span>
</Flex>
);
};
return ( return (
<> <>
<div data-field="template"></div> <div data-field="template"></div>
@@ -135,9 +157,11 @@ const Basic: React.FC<BasicProps> = ({ page = 'template' }) => {
})} })}
> >
<InputNumber <InputNumber
label={intl.formatMessage({ label={renderMaxLabel(
id: 'gpuservice.template.containerDisk' intl.formatMessage({ id: 'gpuservice.template.containerDisk' }),
})} onceMaxRequest?.localStorage
)}
max={onceMaxRequest?.localStorage ?? undefined}
/> />
</Form.Item> </Form.Item>
</div> </div>
@@ -149,7 +173,10 @@ const Basic: React.FC<BasicProps> = ({ page = 'template' }) => {
normalize={(value) => (value ? `${value}` : '')} normalize={(value) => (value ? `${value}` : '')}
getValueProps={(value) => ({ value: value ? String(value) : '' })} getValueProps={(value) => ({ value: value ? String(value) : '' })}
> >
<InputNumber label="CPU" /> <InputNumber
label={renderMaxLabel('CPU', onceMaxRequest?.cpu)}
max={onceMaxRequest?.cpu ?? undefined}
/>
</Form.Item> </Form.Item>
</div> </div>
<div style={{ flex: 1 }}> <div style={{ flex: 1 }}>
@@ -161,7 +188,11 @@ const Basic: React.FC<BasicProps> = ({ page = 'template' }) => {
})} })}
> >
<InputNumber <InputNumber
label={intl.formatMessage({ id: 'gpuservice.template.memory' })} label={renderMaxLabel(
intl.formatMessage({ id: 'gpuservice.template.memory' }),
onceMaxRequest?.memory
)}
max={onceMaxRequest?.memory ?? undefined}
/> />
</Form.Item> </Form.Item>
</div> </div>
+37 -10
View File
@@ -24,63 +24,90 @@ export const ManufacturerMap = {
[GPUDriverMap.THEAD]: 'vendor.thead' [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< export const GPUsConfigs: Record<
string, string,
{ label: string; value: string; runtime: string; driver: string } {
label: string;
value: string;
runtime: string;
driver: string;
gpuVendor?: string;
}
> = { > = {
[GPUDriverMap.NVIDIA]: { [GPUDriverMap.NVIDIA]: {
label: 'NVIDIA', // this label is used in echo command, do not translate label: 'NVIDIA', // this label is used in echo command, do not translate
value: GPUDriverMap.NVIDIA, value: GPUDriverMap.NVIDIA,
runtime: 'nvidia', runtime: 'nvidia',
driver: 'nvidia-smi' driver: 'nvidia-smi',
gpuVendor: 'nvidia'
}, },
[GPUDriverMap.AMD]: { [GPUDriverMap.AMD]: {
label: 'AMD', label: 'AMD',
value: GPUDriverMap.AMD, value: GPUDriverMap.AMD,
runtime: 'amd', runtime: 'amd',
driver: 'amd-smi static' driver: 'amd-smi static',
gpuVendor: 'amd'
}, },
[GPUDriverMap.ASCEND]: { [GPUDriverMap.ASCEND]: {
label: 'Ascend', label: 'Ascend',
value: GPUDriverMap.ASCEND, value: GPUDriverMap.ASCEND,
runtime: 'ascend', runtime: 'ascend',
driver: 'npu-smi info' driver: 'npu-smi info',
gpuVendor: 'ascend'
}, },
[GPUDriverMap.HYGON]: { [GPUDriverMap.HYGON]: {
label: 'Hygon', label: 'Hygon',
value: GPUDriverMap.HYGON, value: GPUDriverMap.HYGON,
runtime: '', // TODO: confirm runtime name runtime: '', // TODO: confirm runtime name
driver: 'hy-smi' driver: 'hy-smi',
gpuVendor: 'hygon'
}, },
[GPUDriverMap.MOORE_THREADS]: { [GPUDriverMap.MOORE_THREADS]: {
label: 'Moore Threads', label: 'Moore Threads',
value: GPUDriverMap.MOORE_THREADS, value: GPUDriverMap.MOORE_THREADS,
runtime: 'mthreads', runtime: 'mthreads',
driver: 'mthreads-gmi' driver: 'mthreads-gmi',
gpuVendor: 'mthreads'
}, },
[GPUDriverMap.ILUVATAR]: { [GPUDriverMap.ILUVATAR]: {
label: 'Iluvatar', label: 'Iluvatar',
value: GPUDriverMap.ILUVATAR, value: GPUDriverMap.ILUVATAR,
runtime: 'iluvatar', runtime: 'iluvatar',
driver: 'ixsmi' driver: 'ixsmi',
gpuVendor: 'iluvatar'
}, },
[GPUDriverMap.CAMBRICON]: { [GPUDriverMap.CAMBRICON]: {
label: 'Cambricon', label: 'Cambricon',
value: GPUDriverMap.CAMBRICON, value: GPUDriverMap.CAMBRICON,
runtime: 'cambricon', runtime: 'cambricon',
driver: 'cnmon info' driver: 'cnmon info',
gpuVendor: 'cambricon'
}, },
[GPUDriverMap.METAX]: { [GPUDriverMap.METAX]: {
label: 'Metax', label: 'Metax',
value: GPUDriverMap.METAX, value: GPUDriverMap.METAX,
runtime: 'metax', runtime: 'metax',
driver: 'mx-smi' driver: 'mx-smi',
gpuVendor: 'metax'
}, },
[GPUDriverMap.THEAD]: { [GPUDriverMap.THEAD]: {
label: 'T-Head PPU', label: 'T-Head PPU',
value: GPUDriverMap.THEAD, value: GPUDriverMap.THEAD,
runtime: '', // TODO: confirm runtime name runtime: '', // TODO: confirm runtime name
driver: 'ppu-smi' driver: 'ppu-smi',
gpuVendor: 'thead'
} }
}; };
-4
View File
@@ -10,10 +10,6 @@ import { extraRequestInterceptors } from './request.extensions';
const NoBaseURLAPIs = ['/auth', '/v1', '/version', '/proxy', '/update']; const NoBaseURLAPIs = ['/auth', '/v1', '/version', '/proxy', '/update'];
export const requestConfig: RequestConfig = { export const requestConfig: RequestConfig = {
headers: {
'Content-Security-Policy': "frame-ancestors 'self'",
'X-Frame-Options': 'SAMEORIGIN'
},
errorConfig: { errorConfig: {
errorThrower: (res: any) => { errorThrower: (res: any) => {
// to do something // to do something