feat: yaml editor
This commit is contained in:
+6
-13
@@ -1,5 +1,6 @@
|
|||||||
import { defineConfig } from '@umijs/max';
|
import { defineConfig } from '@umijs/max';
|
||||||
import keepAlive from './keep-alive';
|
import keepAlive from './keep-alive';
|
||||||
|
import { compressionPluginConfig, monacoPluginConfig } from './plugins';
|
||||||
import proxy from './proxy';
|
import proxy from './proxy';
|
||||||
import routes from './routes';
|
import routes from './routes';
|
||||||
import { getBranchInfo } from './utils';
|
import { getBranchInfo } from './utils';
|
||||||
@@ -29,7 +30,7 @@ export default defineConfig({
|
|||||||
defaultSizes: 'parsed' // stat // gzip
|
defaultSizes: 'parsed' // stat // gzip
|
||||||
},
|
},
|
||||||
mfsu: {
|
mfsu: {
|
||||||
exclude: ['lodash', 'ml-pca']
|
exclude: ['lodash', 'ml-pca', 'monaco-editor']
|
||||||
},
|
},
|
||||||
base: process.env.npm_config_base || '/',
|
base: process.env.npm_config_base || '/',
|
||||||
...(isProduction
|
...(isProduction
|
||||||
@@ -56,21 +57,13 @@ export default defineConfig({
|
|||||||
config.output
|
config.output
|
||||||
.filename(`js/[name].${t}.js`)
|
.filename(`js/[name].${t}.js`)
|
||||||
.chunkFilename(`js/[name].${t}.chunk.js`);
|
.chunkFilename(`js/[name].${t}.chunk.js`);
|
||||||
config
|
compressionPluginConfig(config);
|
||||||
.plugin('compression-webpack-plugin')
|
|
||||||
.use(CompressionWebpackPlugin, [
|
|
||||||
{
|
|
||||||
filename: '[path][base].gz',
|
|
||||||
algorithm: 'gzip',
|
|
||||||
test: /\.(js|css|html|svg)$/,
|
|
||||||
threshold: 10240,
|
|
||||||
minRatio: 0.8
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
|
chainWebpack(config) {
|
||||||
|
monacoPluginConfig(config);
|
||||||
|
},
|
||||||
favicons: ['/static/favicon.png'],
|
favicons: ['/static/favicon.png'],
|
||||||
jsMinifier: 'terser',
|
jsMinifier: 'terser',
|
||||||
cssMinifier: 'cssnano',
|
cssMinifier: 'cssnano',
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin';
|
||||||
|
const CompressionWebpackPlugin = require('compression-webpack-plugin');
|
||||||
|
|
||||||
|
export function monacoPluginConfig(config: any) {
|
||||||
|
return config
|
||||||
|
.plugin('monaco-editor-webpack-plugin')
|
||||||
|
.use(MonacoWebpackPlugin, [
|
||||||
|
{
|
||||||
|
languages: ['yaml'],
|
||||||
|
customLanguages: [
|
||||||
|
{
|
||||||
|
label: 'yaml',
|
||||||
|
entry: 'monaco-yaml',
|
||||||
|
worker: {
|
||||||
|
id: 'monaco-yaml/yamlWorker',
|
||||||
|
entry: 'monaco-yaml/yaml.worker'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compressionPluginConfig(config: any) {
|
||||||
|
return config
|
||||||
|
.plugin('compression-webpack-plugin')
|
||||||
|
.use(CompressionWebpackPlugin, [
|
||||||
|
{
|
||||||
|
filename: '[path][base].gz',
|
||||||
|
algorithm: 'gzip',
|
||||||
|
test: /\.(js|css|html|svg)$/,
|
||||||
|
threshold: 10240,
|
||||||
|
minRatio: 0.8
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -114,6 +114,7 @@ export default [
|
|||||||
selectedIcon: 'icon-backend-filled',
|
selectedIcon: 'icon-backend-filled',
|
||||||
defaultIcon: 'icon-backend',
|
defaultIcon: 'icon-backend',
|
||||||
access: 'canSeeAdmin',
|
access: 'canSeeAdmin',
|
||||||
|
subMenu: ['/models/backends/create'],
|
||||||
component: './backends/list'
|
component: './backends/list'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -46,6 +46,7 @@
|
|||||||
"has-ansi": "^5.0.1",
|
"has-ansi": "^5.0.1",
|
||||||
"highlight.js": "^11.10.0",
|
"highlight.js": "^11.10.0",
|
||||||
"jotai": "^2.8.4",
|
"jotai": "^2.8.4",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
"jszip": "^3.10.1",
|
"jszip": "^3.10.1",
|
||||||
"katex": "^0.16.21",
|
"katex": "^0.16.21",
|
||||||
"lamejs": "github:zhuker/lamejs",
|
"lamejs": "github:zhuker/lamejs",
|
||||||
@@ -55,6 +56,9 @@
|
|||||||
"marked": "^14.1.0",
|
"marked": "^14.1.0",
|
||||||
"ml-dataset-iris": "^1.2.1",
|
"ml-dataset-iris": "^1.2.1",
|
||||||
"ml-pca": "^4.1.1",
|
"ml-pca": "^4.1.1",
|
||||||
|
"monaco-editor": "^0.53.0",
|
||||||
|
"monaco-editor-webpack-plugin": "^7.1.0",
|
||||||
|
"monaco-yaml": "^5.4.0",
|
||||||
"numeral": "^2.0.6",
|
"numeral": "^2.0.6",
|
||||||
"overlayscrollbars": "^2.10.0",
|
"overlayscrollbars": "^2.10.0",
|
||||||
"overlayscrollbars-react": "^0.5.6",
|
"overlayscrollbars-react": "^0.5.6",
|
||||||
|
|||||||
Generated
+111
-6
@@ -29,7 +29,7 @@ dependencies:
|
|||||||
version: 0.11.13
|
version: 0.11.13
|
||||||
'@monaco-editor/react':
|
'@monaco-editor/react':
|
||||||
specifier: ^4.6.0
|
specifier: ^4.6.0
|
||||||
version: 4.7.0(monaco-editor@0.52.2)(react-dom@18.3.1)(react@18.3.1)
|
version: 4.7.0(monaco-editor@0.53.0)(react-dom@18.3.1)(react@18.3.1)
|
||||||
'@orcid/bibtex-parse-js':
|
'@orcid/bibtex-parse-js':
|
||||||
specifier: ^0.0.25
|
specifier: ^0.0.25
|
||||||
version: 0.0.25
|
version: 0.0.25
|
||||||
@@ -99,6 +99,9 @@ dependencies:
|
|||||||
jotai:
|
jotai:
|
||||||
specifier: ^2.8.4
|
specifier: ^2.8.4
|
||||||
version: 2.13.1(@babel/core@7.28.3)(@types/react@18.3.24)(react@18.3.1)
|
version: 2.13.1(@babel/core@7.28.3)(@types/react@18.3.24)(react@18.3.1)
|
||||||
|
js-yaml:
|
||||||
|
specifier: ^4.1.0
|
||||||
|
version: 4.1.0
|
||||||
jszip:
|
jszip:
|
||||||
specifier: ^3.10.1
|
specifier: ^3.10.1
|
||||||
version: 3.10.1
|
version: 3.10.1
|
||||||
@@ -126,6 +129,15 @@ dependencies:
|
|||||||
ml-pca:
|
ml-pca:
|
||||||
specifier: ^4.1.1
|
specifier: ^4.1.1
|
||||||
version: 4.1.1
|
version: 4.1.1
|
||||||
|
monaco-editor:
|
||||||
|
specifier: ^0.53.0
|
||||||
|
version: 0.53.0
|
||||||
|
monaco-editor-webpack-plugin:
|
||||||
|
specifier: ^7.1.0
|
||||||
|
version: 7.1.0(monaco-editor@0.53.0)(webpack@5.101.3)
|
||||||
|
monaco-yaml:
|
||||||
|
specifier: ^5.4.0
|
||||||
|
version: 5.4.0(monaco-editor@0.53.0)
|
||||||
numeral:
|
numeral:
|
||||||
specifier: ^2.0.6
|
specifier: ^2.0.6
|
||||||
version: 2.0.6
|
version: 2.0.6
|
||||||
@@ -2699,7 +2711,7 @@ packages:
|
|||||||
state-local: 1.0.7
|
state-local: 1.0.7
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@monaco-editor/react@4.7.0(monaco-editor@0.52.2)(react-dom@18.3.1)(react@18.3.1):
|
/@monaco-editor/react@4.7.0(monaco-editor@0.53.0)(react-dom@18.3.1)(react@18.3.1):
|
||||||
resolution: {integrity: sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==, tarball: https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz}
|
resolution: {integrity: sha512-cyzXQCtO47ydzxpQtCGSQGOC8Gk3ZUeBXFAxD+CWXYFo5OqZyZUonFl0DwUlTyAfRHntBfw2p3w4s9R6oe1eCA==, tarball: https://registry.npmjs.org/@monaco-editor/react/-/react-4.7.0.tgz}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
monaco-editor: '>= 0.25.0 < 1'
|
monaco-editor: '>= 0.25.0 < 1'
|
||||||
@@ -2707,7 +2719,7 @@ packages:
|
|||||||
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@monaco-editor/loader': 1.5.0
|
'@monaco-editor/loader': 1.5.0
|
||||||
monaco-editor: 0.52.2
|
monaco-editor: 0.53.0
|
||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
dev: false
|
dev: false
|
||||||
@@ -3637,6 +3649,10 @@ packages:
|
|||||||
/@types/stylis@4.2.7:
|
/@types/stylis@4.2.7:
|
||||||
resolution: {integrity: sha512-VgDNokpBoKF+wrdvhAAfS55OMQpL6QRglwTwNC3kIgBrzZxA4WsFj+2eLfEA/uMUDzBcEhYmjSbwQakn/i3ajA==, tarball: https://registry.npmjs.org/@types/stylis/-/stylis-4.2.7.tgz}
|
resolution: {integrity: sha512-VgDNokpBoKF+wrdvhAAfS55OMQpL6QRglwTwNC3kIgBrzZxA4WsFj+2eLfEA/uMUDzBcEhYmjSbwQakn/i3ajA==, tarball: https://registry.npmjs.org/@types/stylis/-/stylis-4.2.7.tgz}
|
||||||
|
|
||||||
|
/@types/trusted-types@1.0.6:
|
||||||
|
resolution: {integrity: sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw==, tarball: https://registry.npmjs.org/@types/trusted-types/-/trusted-types-1.0.6.tgz}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/trusted-types@2.0.7:
|
/@types/trusted-types@2.0.7:
|
||||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==, tarball: https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz}
|
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==, tarball: https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz}
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
@@ -9332,6 +9348,10 @@ packages:
|
|||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
/jsonc-parser@3.3.1:
|
||||||
|
resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==, tarball: https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/jsonfile@6.2.0:
|
/jsonfile@6.2.0:
|
||||||
resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==, tarball: https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz}
|
resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==, tarball: https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -10481,8 +10501,66 @@ packages:
|
|||||||
/moment@2.30.1:
|
/moment@2.30.1:
|
||||||
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==, tarball: https://registry.npmjs.org/moment/-/moment-2.30.1.tgz}
|
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==, tarball: https://registry.npmjs.org/moment/-/moment-2.30.1.tgz}
|
||||||
|
|
||||||
/monaco-editor@0.52.2:
|
/monaco-editor-webpack-plugin@7.1.0(monaco-editor@0.53.0)(webpack@5.101.3):
|
||||||
resolution: {integrity: sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ==, tarball: https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.52.2.tgz}
|
resolution: {integrity: sha512-ZjnGINHN963JQkFqjjcBtn1XBtUATDZBMgNQhDQwd78w2ukRhFXAPNgWuacaQiDZsUr4h1rWv5Mv6eriKuOSzA==, tarball: https://registry.npmjs.org/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-7.1.0.tgz}
|
||||||
|
peerDependencies:
|
||||||
|
monaco-editor: '>= 0.31.0'
|
||||||
|
webpack: ^4.5.0 || 5.x
|
||||||
|
dependencies:
|
||||||
|
loader-utils: 2.0.4
|
||||||
|
monaco-editor: 0.53.0
|
||||||
|
webpack: 5.101.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/monaco-editor@0.53.0:
|
||||||
|
resolution: {integrity: sha512-0WNThgC6CMWNXXBxTbaYYcunj08iB5rnx4/G56UOPeL9UVIUGGHA1GR0EWIh9Ebabj7NpCRawQ5b0hfN1jQmYQ==, tarball: https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.53.0.tgz}
|
||||||
|
dependencies:
|
||||||
|
'@types/trusted-types': 1.0.6
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/monaco-languageserver-types@0.4.0:
|
||||||
|
resolution: {integrity: sha512-QQ3BZiU5LYkJElGncSNb5AKoJ/LCs6YBMCJMAz9EA7v+JaOdn3kx2cXpPTcZfKA5AEsR0vc97sAw+5mdNhVBmw==, tarball: https://registry.npmjs.org/monaco-languageserver-types/-/monaco-languageserver-types-0.4.0.tgz}
|
||||||
|
dependencies:
|
||||||
|
monaco-types: 0.1.0
|
||||||
|
vscode-languageserver-protocol: 3.17.5
|
||||||
|
vscode-uri: 3.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/monaco-marker-data-provider@1.2.4:
|
||||||
|
resolution: {integrity: sha512-4DsPgsAqpTyUDs3humXRBPUJoihTv+L6v9aupQWD80X2YXaCXUd11mWYeSCYHuPgdUmjFaNWCEOjQ6ewf/QA1Q==, tarball: https://registry.npmjs.org/monaco-marker-data-provider/-/monaco-marker-data-provider-1.2.4.tgz}
|
||||||
|
dependencies:
|
||||||
|
monaco-types: 0.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/monaco-types@0.1.0:
|
||||||
|
resolution: {integrity: sha512-aWK7SN9hAqNYi0WosPoMjenMeXJjwCxDibOqWffyQ/qXdzB/86xshGQobRferfmNz7BSNQ8GB0MD0oby9/5fTQ==, tarball: https://registry.npmjs.org/monaco-types/-/monaco-types-0.1.0.tgz}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/monaco-worker-manager@2.0.1(monaco-editor@0.53.0):
|
||||||
|
resolution: {integrity: sha512-kdPL0yvg5qjhKPNVjJoym331PY/5JC11aPJXtCZNwWRvBr6jhkIamvYAyiY5P1AWFmNOy0aRDRoMdZfa71h8kg==, tarball: https://registry.npmjs.org/monaco-worker-manager/-/monaco-worker-manager-2.0.1.tgz}
|
||||||
|
peerDependencies:
|
||||||
|
monaco-editor: '>=0.30.0'
|
||||||
|
dependencies:
|
||||||
|
monaco-editor: 0.53.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/monaco-yaml@5.4.0(monaco-editor@0.53.0):
|
||||||
|
resolution: {integrity: sha512-tuBVDy1KAPrgO905GHTItu8AaA5bIzF5S4X0JVRAE/D66FpRhkDUk7tKi5bwKMVTTugtpMLsXN4ewh4CgE/FtQ==, tarball: https://registry.npmjs.org/monaco-yaml/-/monaco-yaml-5.4.0.tgz}
|
||||||
|
peerDependencies:
|
||||||
|
monaco-editor: '>=0.36'
|
||||||
|
dependencies:
|
||||||
|
jsonc-parser: 3.3.1
|
||||||
|
monaco-editor: 0.53.0
|
||||||
|
monaco-languageserver-types: 0.4.0
|
||||||
|
monaco-marker-data-provider: 1.2.4
|
||||||
|
monaco-types: 0.1.0
|
||||||
|
monaco-worker-manager: 2.0.1(monaco-editor@0.53.0)
|
||||||
|
path-browserify: 1.0.1
|
||||||
|
prettier: 3.6.2
|
||||||
|
vscode-languageserver-textdocument: 1.0.12
|
||||||
|
vscode-languageserver-types: 3.17.5
|
||||||
|
vscode-uri: 3.1.0
|
||||||
|
yaml: 2.8.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/mrmime@2.0.1:
|
/mrmime@2.0.1:
|
||||||
@@ -11078,6 +11156,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==, tarball: https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz}
|
resolution: {integrity: sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==, tarball: https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/path-browserify@1.0.1:
|
||||||
|
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==, tarball: https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/path-exists@4.0.0:
|
/path-exists@4.0.0:
|
||||||
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, tarball: https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz}
|
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, tarball: https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -15580,6 +15662,30 @@ packages:
|
|||||||
resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==, tarball: https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz}
|
resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==, tarball: https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/vscode-jsonrpc@8.2.0:
|
||||||
|
resolution: {integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==, tarball: https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz}
|
||||||
|
engines: {node: '>=14.0.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/vscode-languageserver-protocol@3.17.5:
|
||||||
|
resolution: {integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==, tarball: https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz}
|
||||||
|
dependencies:
|
||||||
|
vscode-jsonrpc: 8.2.0
|
||||||
|
vscode-languageserver-types: 3.17.5
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/vscode-languageserver-textdocument@1.0.12:
|
||||||
|
resolution: {integrity: sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==, tarball: https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/vscode-languageserver-types@3.17.5:
|
||||||
|
resolution: {integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==, tarball: https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/vscode-uri@3.1.0:
|
||||||
|
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==, tarball: https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/walker@1.0.8:
|
/walker@1.0.8:
|
||||||
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==, tarball: https://registry.npmjs.org/walker/-/walker-1.0.8.tgz}
|
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==, tarball: https://registry.npmjs.org/walker/-/walker-1.0.8.tgz}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -15873,7 +15979,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==, tarball: https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz}
|
resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==, tarball: https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz}
|
||||||
engines: {node: '>= 14.6'}
|
engines: {node: '>= 14.6'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
|
||||||
|
|
||||||
/yargs-parser@20.2.9:
|
/yargs-parser@20.2.9:
|
||||||
resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==, tarball: https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz}
|
resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==, tarball: https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -55,9 +55,7 @@ const CopyButton: React.FC<CopyButtonProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
clipboardRef.current.on('error', (e: any) => {
|
clipboardRef.current.on('error', (e: any) => {
|
||||||
message.success(
|
message.error(intl.formatMessage({ id: 'common.copy.fail' }) as string);
|
||||||
intl.formatMessage({ id: 'common.copy.fail' }) as string
|
|
||||||
);
|
|
||||||
e.clearSelection();
|
e.clearSelection();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,74 +1,66 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import CopyButton from '../copy-button';
|
|
||||||
import SegmentLine from '../segment-line';
|
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
const Wrapper = styled.div<{ $height: number }>`
|
const HeaderWrapper = styled.div<{ $height?: number }>`
|
||||||
height: ${(props) => props.$height}px;
|
height: ${(props) => (props.$height ? `${props.$height}px` : 'auto')};
|
||||||
|
display: flex;
|
||||||
|
padding-block: 0;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
border-radius: var(--border-radius-mini);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&.bordered {
|
||||||
|
border: 1px solid var(--ant-color-border);
|
||||||
|
}
|
||||||
|
&.borderless {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.code-pre {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
.scrollbar {
|
||||||
|
.slider {
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface EditorwrapProps {
|
interface EditorwrapProps {
|
||||||
headerHeight?: number;
|
headerHeight?: number;
|
||||||
header?: React.ReactNode;
|
header?: React.ReactNode;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
showHeader?: boolean;
|
variant?: 'bordered' | 'borderless';
|
||||||
copyText: string;
|
|
||||||
defaultValue?: string;
|
|
||||||
langOptions?: Global.BaseOption<string | number>[];
|
|
||||||
styles?: {
|
styles?: {
|
||||||
wrapper?: React.CSSProperties;
|
wrapper?: React.CSSProperties;
|
||||||
header?: React.CSSProperties;
|
header?: React.CSSProperties;
|
||||||
content?: React.CSSProperties;
|
content?: React.CSSProperties;
|
||||||
};
|
};
|
||||||
onChangeLang?: (value: string | number) => void;
|
|
||||||
}
|
}
|
||||||
const EditorWrap: React.FC<EditorwrapProps> = ({
|
const EditorWrap: React.FC<EditorwrapProps> = ({
|
||||||
headerHeight = 40,
|
headerHeight = 40,
|
||||||
header,
|
header,
|
||||||
children,
|
children,
|
||||||
copyText,
|
variant = 'borderless',
|
||||||
langOptions = [],
|
styles = {}
|
||||||
onChangeLang,
|
|
||||||
defaultValue,
|
|
||||||
styles = {},
|
|
||||||
showHeader = true
|
|
||||||
}) => {
|
}) => {
|
||||||
const handleChangeLang = (value: string | number) => {
|
|
||||||
onChangeLang?.(value);
|
|
||||||
};
|
|
||||||
const renderHeader = () => {
|
|
||||||
if (header) {
|
|
||||||
return <div className="editor-header">{header}</div>;
|
|
||||||
}
|
|
||||||
if (showHeader) {
|
|
||||||
return (
|
|
||||||
<Wrapper className="editor-header" $height={headerHeight}>
|
|
||||||
<SegmentLine
|
|
||||||
height={headerHeight}
|
|
||||||
defaultValue={defaultValue}
|
|
||||||
size="small"
|
|
||||||
options={langOptions}
|
|
||||||
onChange={handleChangeLang}
|
|
||||||
></SegmentLine>
|
|
||||||
<CopyButton
|
|
||||||
text={copyText}
|
|
||||||
size="small"
|
|
||||||
style={{
|
|
||||||
color: 'rgba(255,255,255,.7)'
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Wrapper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
return (
|
return (
|
||||||
<div className="editor-wrap" style={{ ...styles.wrapper }}>
|
<Wrapper
|
||||||
{renderHeader()}
|
style={{ ...styles.wrapper }}
|
||||||
<div className="editor-content">{children}</div>
|
className={classNames({
|
||||||
</div>
|
bordered: variant === 'bordered',
|
||||||
|
borderless: variant === 'borderless'
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{header && <HeaderWrapper $height={headerHeight}>{header}</HeaderWrapper>}
|
||||||
|
<div>{children}</div>
|
||||||
|
</Wrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(EditorWrap);
|
export default EditorWrap;
|
||||||
|
|||||||
@@ -1,30 +1,39 @@
|
|||||||
|
import { LoadingOutlined } from '@ant-design/icons';
|
||||||
import Editor from '@monaco-editor/react';
|
import Editor from '@monaco-editor/react';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, {
|
||||||
|
forwardRef,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useRef
|
||||||
|
} from 'react';
|
||||||
import EditorWrap from '../editor-wrap';
|
import EditorWrap from '../editor-wrap';
|
||||||
|
|
||||||
interface ViewerProps {
|
interface ViewerProps {
|
||||||
|
ref?: any;
|
||||||
lang: string;
|
lang: string;
|
||||||
defaultLang?: string;
|
defaultLang?: string;
|
||||||
langOptions?: Global.BaseOption<string>[];
|
|
||||||
config?: any;
|
config?: any;
|
||||||
value: string;
|
value: string;
|
||||||
height?: string | number;
|
height?: string | number;
|
||||||
theme?: string;
|
theme?: string;
|
||||||
showHeader?: boolean;
|
header?: React.ReactNode;
|
||||||
|
placeholder?: string;
|
||||||
|
variant?: 'bordered' | 'borderless';
|
||||||
}
|
}
|
||||||
|
|
||||||
const ViewerEditor: React.FC<ViewerProps> = (props) => {
|
const ViewerEditor: React.FC<ViewerProps> = forwardRef((props, ref) => {
|
||||||
const editorRef = useRef<any>(null);
|
|
||||||
const {
|
const {
|
||||||
lang,
|
lang,
|
||||||
value,
|
value,
|
||||||
config,
|
config,
|
||||||
langOptions,
|
|
||||||
defaultLang,
|
defaultLang,
|
||||||
height = 380,
|
height = 380,
|
||||||
showHeader
|
theme = 'vs-dark',
|
||||||
|
header,
|
||||||
|
variant = 'borderless',
|
||||||
|
placeholder
|
||||||
} = props;
|
} = props;
|
||||||
const [langType, setLangType] = useState(defaultLang);
|
|
||||||
|
const editorRef = useRef<any>(null);
|
||||||
|
|
||||||
const handleBeforeMount = (monaco: any) => {
|
const handleBeforeMount = (monaco: any) => {
|
||||||
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
|
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
|
||||||
@@ -36,12 +45,8 @@ const ViewerEditor: React.FC<ViewerProps> = (props) => {
|
|||||||
|
|
||||||
const handleEditorDidMount = (editor: any, monaco: any) => {
|
const handleEditorDidMount = (editor: any, monaco: any) => {
|
||||||
editorRef.current = editor;
|
editorRef.current = editor;
|
||||||
console.log('loaded====', editor, monaco);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleOnChangeLang = (value: string) => {
|
|
||||||
setLangType(value);
|
|
||||||
};
|
|
||||||
const formatCode = () => {
|
const formatCode = () => {
|
||||||
if (editorRef.current) {
|
if (editorRef.current) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -55,35 +60,50 @@ const ViewerEditor: React.FC<ViewerProps> = (props) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
format: () => {
|
||||||
|
formatCode();
|
||||||
|
},
|
||||||
|
getValue: () => {
|
||||||
|
return editorRef.current?.getValue?.();
|
||||||
|
},
|
||||||
|
setValue: (val: string) => {
|
||||||
|
editorRef.current?.setValue?.(val);
|
||||||
|
},
|
||||||
|
editor: editorRef.current
|
||||||
|
}));
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
formatCode();
|
formatCode();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const lineCount = editorRef.current?.getModel().getLineCount(); // 获取总行数
|
const lineCount = editorRef.current?.getModel().getLineCount();
|
||||||
editorRef.current?.revealLine(lineCount); // 滚动到最后一行
|
editorRef.current?.revealLine(lineCount);
|
||||||
}, 100); // 可以调整延时,确保编辑器完全加载
|
}, 100);
|
||||||
}, [value]);
|
}, [value]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EditorWrap
|
<EditorWrap header={header} variant={variant}>
|
||||||
copyText={value}
|
|
||||||
langOptions={langOptions}
|
|
||||||
defaultValue={lang}
|
|
||||||
showHeader={showHeader}
|
|
||||||
onChangeLang={handleOnChangeLang}
|
|
||||||
>
|
|
||||||
<Editor
|
<Editor
|
||||||
height={height}
|
height={height}
|
||||||
theme="vs-dark"
|
theme={theme}
|
||||||
className="monaco-editor"
|
className="monaco-editor"
|
||||||
defaultLanguage={defaultLang}
|
defaultLanguage={defaultLang}
|
||||||
language={langType}
|
language={lang}
|
||||||
value={value}
|
value={value}
|
||||||
options={config}
|
options={{
|
||||||
|
minimap: { enabled: false },
|
||||||
|
scrollbar: {
|
||||||
|
verticalScrollbarSize: 6,
|
||||||
|
horizontalScrollbarSize: 6
|
||||||
|
},
|
||||||
|
placeholder: placeholder
|
||||||
|
}}
|
||||||
|
loading={<LoadingOutlined style={{ fontSize: 24 }}></LoadingOutlined>}
|
||||||
beforeMount={handleBeforeMount}
|
beforeMount={handleBeforeMount}
|
||||||
onMount={handleEditorDidMount}
|
onMount={handleEditorDidMount}
|
||||||
/>
|
/>
|
||||||
</EditorWrap>
|
</EditorWrap>
|
||||||
);
|
);
|
||||||
};
|
});
|
||||||
|
|
||||||
export default React.memo(ViewerEditor);
|
export default ViewerEditor;
|
||||||
@@ -45,7 +45,11 @@ const icons = {
|
|||||||
Ascend: React.createElement(IconFont, { type: 'icon-ascend' }),
|
Ascend: React.createElement(IconFont, { type: 'icon-ascend' }),
|
||||||
Catalog1: React.createElement(IconFont, { type: 'icon-catalog1' }),
|
Catalog1: React.createElement(IconFont, { type: 'icon-catalog1' }),
|
||||||
AMD: React.createElement(IconFont, { type: 'icon-amd' }),
|
AMD: React.createElement(IconFont, { type: 'icon-amd' }),
|
||||||
KubernetesFilled: React.createElement(IconFont, { type: 'icon-k8s-filled' })
|
KubernetesFilled: React.createElement(IconFont, { type: 'icon-k8s-filled' }),
|
||||||
|
EditContent: React.createElement(IconFont, { type: 'icon-edit-content' }),
|
||||||
|
Yaml: React.createElement(IconFont, { type: 'icon-code_block' }),
|
||||||
|
Version: React.createElement(IconFont, { type: 'icon-version' }),
|
||||||
|
Parameter: React.createElement(IconFont, { type: 'icon-parameters' })
|
||||||
};
|
};
|
||||||
|
|
||||||
export default icons;
|
export default icons;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createFromIconfontCN } from '@ant-design/icons';
|
|||||||
// import './iconfont/iconfont.js';
|
// import './iconfont/iconfont.js';
|
||||||
|
|
||||||
const IconFont = createFromIconfontCN({
|
const IconFont = createFromIconfontCN({
|
||||||
scriptUrl: '//at.alicdn.com/t/c/font_4613488_wt33o3w25g.js'
|
scriptUrl: '//at.alicdn.com/t/c/font_4613488_c06q7b0n32v.js'
|
||||||
});
|
});
|
||||||
|
|
||||||
export default IconFont;
|
export default IconFont;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
DownOutlined,
|
DownOutlined,
|
||||||
PlusOutlined,
|
PlusOutlined,
|
||||||
|
SearchOutlined,
|
||||||
SyncOutlined
|
SyncOutlined
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
@@ -170,6 +171,11 @@ export const FilterBar: React.FC<FilterBarProps> = (props) => {
|
|||||||
left={
|
left={
|
||||||
<Space>
|
<Space>
|
||||||
<Input
|
<Input
|
||||||
|
prefix={
|
||||||
|
<SearchOutlined
|
||||||
|
style={{ color: 'var(--ant-color-text-placeholder)' }}
|
||||||
|
></SearchOutlined>
|
||||||
|
}
|
||||||
placeholder={
|
placeholder={
|
||||||
inputHolder ||
|
inputHolder ||
|
||||||
intl.formatMessage({
|
intl.formatMessage({
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ const SealTextArea: React.FC<InputTextareaProps & SealFormItemProps> = (
|
|||||||
>
|
>
|
||||||
<Input.TextArea
|
<Input.TextArea
|
||||||
{...rest}
|
{...rest}
|
||||||
|
spellCheck={rest.spellCheck ?? false}
|
||||||
autoSize={autoSize}
|
autoSize={autoSize}
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
style={{ minHeight: '80px', ...style }}
|
style={{ minHeight: '80px', ...style }}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ const SegmentedQwrapper = styled.div<{ $height: number }>`
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
top: unset;
|
top: unset;
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.ant-segmented-item-selected {
|
.ant-segmented-item-selected {
|
||||||
@@ -55,6 +56,7 @@ const SegmentedQwrapper = styled.div<{ $height: number }>`
|
|||||||
color: var(--color-white-primary);
|
color: var(--color-white-primary);
|
||||||
&::after {
|
&::after {
|
||||||
height: 2px;
|
height: 2px;
|
||||||
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,6 +70,7 @@ const SegmentLine: React.FC<SegmentLineProps> = (props) => {
|
|||||||
className = 'segment-line',
|
className = 'segment-line',
|
||||||
...rest
|
...rest
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SegmentedQwrapper $height={height}>
|
<SegmentedQwrapper $height={height}>
|
||||||
<Segmented
|
<Segmented
|
||||||
|
|||||||
@@ -588,13 +588,6 @@ body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// =============== segment start==============
|
|
||||||
.ant-segmented .ant-segmented-item::after {
|
|
||||||
border-radius: var(--border-radius-mini);
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============== segment end ===============
|
|
||||||
|
|
||||||
.background {
|
.background {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import IconFont from '@/components/icon-font';
|
||||||
|
import { Collapse, CollapseProps } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const CollapseInner = styled(Collapse)`
|
||||||
|
.ant-collapse-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px !important;
|
||||||
|
padding-inline: 5px !important;
|
||||||
|
padding-block: 8px !important;
|
||||||
|
border-radius: var(--border-radius-base) !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
font-weight: 600 !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--ant-color-fill-tertiary) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-collapse-content-box {
|
||||||
|
padding-inline: 0 !important;
|
||||||
|
padding-block: 0 !important;
|
||||||
|
}
|
||||||
|
.ant-collapse-header-text {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CollapsePanel: React.FC<{ items: CollapseProps['items'] }> = ({
|
||||||
|
items
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<CollapseInner
|
||||||
|
expandIconPosition="start"
|
||||||
|
bordered={false}
|
||||||
|
ghost
|
||||||
|
accordion
|
||||||
|
destroyOnHidden={false}
|
||||||
|
expandIcon={({ isActive }) => (
|
||||||
|
<IconFont
|
||||||
|
type="icon-down"
|
||||||
|
rotate={isActive ? 0 : -90}
|
||||||
|
style={{ fontSize: '14px' }}
|
||||||
|
></IconFont>
|
||||||
|
)}
|
||||||
|
items={items}
|
||||||
|
></CollapseInner>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CollapsePanel;
|
||||||
+18
-17
@@ -1,6 +1,6 @@
|
|||||||
import useOverlayScroller from '@/hooks/use-overlay-scroller';
|
import useOverlayScroller from '@/hooks/use-overlay-scroller';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import '../style/column-wrapper.less';
|
import './style.less';
|
||||||
|
|
||||||
const ColumnWrapper: React.FC<any> = ({
|
const ColumnWrapper: React.FC<any> = ({
|
||||||
children,
|
children,
|
||||||
@@ -25,24 +25,25 @@ const ColumnWrapper: React.FC<any> = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{footer ? (
|
<div
|
||||||
<div className="column-wrapper-footer">
|
className="column-wrapper-footer"
|
||||||
<div className="column-wrapper" ref={scroller} style={{ maxHeight }}>
|
style={{ height: maxHeight || '100%' }}
|
||||||
<div
|
>
|
||||||
style={{
|
<div
|
||||||
paddingBottom: paddingBottom
|
className="column-wrapper"
|
||||||
}}
|
ref={scroller}
|
||||||
>
|
style={{ padding: '16px 24px' }}
|
||||||
{children}
|
>
|
||||||
</div>
|
<div
|
||||||
|
style={{
|
||||||
|
paddingBottom: footer ? paddingBottom : 0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
</div>
|
</div>
|
||||||
{<div className="footer">{footer}</div>}
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
{footer && <div className="footer">{footer}</div>}
|
||||||
<div className="column-wrapper" ref={scroller} style={{ maxHeight }}>
|
</div>
|
||||||
<div>{children}</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
import CopyButton from '@/components/copy-button';
|
||||||
|
import EditorWrap from '@/components/editor-wrap';
|
||||||
|
import HighlightCode from '@/components/highlight-code';
|
||||||
|
import SegmentLine from '@/components/segment-line';
|
||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
interface ViewerProps {
|
||||||
|
code: string;
|
||||||
|
copyText?: string;
|
||||||
|
options?: Global.BaseOption<string>[];
|
||||||
|
defaultValue?: string;
|
||||||
|
headerHeight?: number;
|
||||||
|
lang?: string;
|
||||||
|
onChange?: (value: string | number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Header = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
padding-inline: 12px 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: var(--color-editor-header-bg);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const CommandViewer: React.FC<ViewerProps> = (props) => {
|
||||||
|
const {
|
||||||
|
code,
|
||||||
|
copyText = '',
|
||||||
|
defaultValue,
|
||||||
|
options = [],
|
||||||
|
headerHeight = 40,
|
||||||
|
lang,
|
||||||
|
onChange
|
||||||
|
} = props || {};
|
||||||
|
|
||||||
|
const [value, setValue] = React.useState<string>(defaultValue || '');
|
||||||
|
|
||||||
|
const handlOnChange = (value: string | number) => {
|
||||||
|
setValue(value as string);
|
||||||
|
onChange?.(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditorWrap
|
||||||
|
header={
|
||||||
|
<Header>
|
||||||
|
<SegmentLine
|
||||||
|
height={headerHeight}
|
||||||
|
defaultValue={defaultValue}
|
||||||
|
value={value}
|
||||||
|
size="small"
|
||||||
|
options={options}
|
||||||
|
onChange={handlOnChange}
|
||||||
|
></SegmentLine>
|
||||||
|
|
||||||
|
<CopyButton
|
||||||
|
text={copyText || code}
|
||||||
|
size="small"
|
||||||
|
style={{
|
||||||
|
color: 'rgba(255,255,255,.7)'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Header>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<HighlightCode
|
||||||
|
height={380}
|
||||||
|
theme="dark"
|
||||||
|
code={code}
|
||||||
|
lang={lang || value}
|
||||||
|
copyable={false}
|
||||||
|
></HighlightCode>
|
||||||
|
</EditorWrap>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CommandViewer;
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
import { PageAction } from '@/config';
|
||||||
|
import { PageActionType } from '@/config/types';
|
||||||
|
import ClusterSteps from '@/pages/cluster-management/components/cluster-steps';
|
||||||
|
import FooterButtons from '@/pages/cluster-management/components/footer-buttons';
|
||||||
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
|
import { useIntl, useNavigate, useSearchParams } from '@umijs/max';
|
||||||
|
import _ from 'lodash';
|
||||||
|
import React, { useMemo, useRef, useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { createBackend } from './apis';
|
||||||
|
import { FormData } from './config/types';
|
||||||
|
import { moduleMap, moduleRegistry } from './step-forms/module-registry';
|
||||||
|
import useStepList from './step-forms/use-step-list';
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Nav = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 72px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 20px;
|
||||||
|
color: var(--ant-color-text-tertiary);
|
||||||
|
.level-2 {
|
||||||
|
color: var(--ant-color-text);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Content = styled.div`
|
||||||
|
width: 600px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const HeaderContainer = styled.div`
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
align-items: center;
|
||||||
|
padding-inline: var(--layout-content-header-inlinepadding);
|
||||||
|
.text {
|
||||||
|
margin-right: 16px;
|
||||||
|
padding-right: 16px;
|
||||||
|
border-right: 1px solid var(--ant-color-split);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 20px;
|
||||||
|
margin-right: 32px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ClusterCreate = () => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const startStep = 0;
|
||||||
|
const steps = useStepList();
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const action =
|
||||||
|
(searchParams.get('action') as PageActionType) || PageAction.CREATE;
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [currentStep, setCurrentStep] = useState<number>(startStep);
|
||||||
|
|
||||||
|
const [extraData, setExtraData] = useState<FormData>({
|
||||||
|
default_version: ''
|
||||||
|
} as FormData);
|
||||||
|
const [formValues, setFormValues] = useState<Record<string, any>>({});
|
||||||
|
|
||||||
|
const formRefs: Record<string, any> = {
|
||||||
|
[moduleMap.BasicForm]: useRef<any>(null),
|
||||||
|
[moduleMap.ParamertersForm]: useRef<any>(null),
|
||||||
|
[moduleMap.VersionConfigForm]: useRef<any>(null)
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStepChange = (newStep: number) => {
|
||||||
|
setCurrentStep(newStep);
|
||||||
|
console.log('step========', newStep);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFormFieldsValue = () => {
|
||||||
|
setFormValues((prev) => {
|
||||||
|
const newFormValues = _.cloneDeep(prev);
|
||||||
|
for (const [formKey, formRef] of Object.entries(formRefs)) {
|
||||||
|
const formValues = formRef?.current?.getFieldsValue?.();
|
||||||
|
if (formValues) {
|
||||||
|
newFormValues[formKey] = formValues;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newFormValues;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onPrevious = () => {
|
||||||
|
getFormFieldsValue();
|
||||||
|
setCurrentStep((prev) => Math.max(prev - 1, 0));
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateForms = async () => {
|
||||||
|
const step = steps[currentStep];
|
||||||
|
const formKeys = step?.showForms || [];
|
||||||
|
if (formKeys?.length > 0) {
|
||||||
|
const results = await Promise.allSettled(
|
||||||
|
formKeys.map((key) => formRefs[key].current?.validateFields())
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('results========', results);
|
||||||
|
|
||||||
|
const resultsMap = new Map<string, any>();
|
||||||
|
results.forEach((result, index) => {
|
||||||
|
if (result.status === 'rejected') {
|
||||||
|
const values = result.reason?.values || {};
|
||||||
|
resultsMap.set(formKeys[index], values);
|
||||||
|
} else if (result.status === 'fulfilled') {
|
||||||
|
resultsMap.set(formKeys[index], result.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const newValues = _.merge(formValues, Object.fromEntries(resultsMap));
|
||||||
|
|
||||||
|
setFormValues(newValues);
|
||||||
|
|
||||||
|
const isValid = results.every((result) => result.status === 'fulfilled');
|
||||||
|
if (!isValid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return Object.assign({}, ...Object.values(newValues));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onNext = async (callback?: (values: FormData) => void) => {
|
||||||
|
try {
|
||||||
|
const result = await validateForms();
|
||||||
|
console.log('results========2', result);
|
||||||
|
if (result) {
|
||||||
|
await callback?.(result as FormData);
|
||||||
|
const step = steps[currentStep];
|
||||||
|
step.beforeNext?.();
|
||||||
|
setCurrentStep((prev) => Math.min(prev + 1, steps.length - 1));
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log('next error:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const resetForm = () => {
|
||||||
|
setFormValues({});
|
||||||
|
for (const [formKey, formRef] of Object.entries(formRefs)) {
|
||||||
|
formRefs[formKey] = React.createRef();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderForms = () => {
|
||||||
|
const step = steps[currentStep];
|
||||||
|
const formKeys = step?.showForms || [];
|
||||||
|
if (formKeys.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return formKeys.map((key) => {
|
||||||
|
console.log('renderForms key========', key, formValues);
|
||||||
|
const FormComponent = moduleRegistry[key];
|
||||||
|
return FormComponent ? (
|
||||||
|
<FormComponent
|
||||||
|
key={key}
|
||||||
|
ref={formRefs[key]}
|
||||||
|
action={action}
|
||||||
|
currentData={formValues[key]}
|
||||||
|
/>
|
||||||
|
) : null;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const showButtons = useMemo(() => {
|
||||||
|
const step = steps[currentStep];
|
||||||
|
return step?.showButtons ? step?.showButtons?.() : {};
|
||||||
|
}, [currentStep, steps]);
|
||||||
|
|
||||||
|
const submit = async (values: FormData) => {
|
||||||
|
const data = {
|
||||||
|
...extraData,
|
||||||
|
...(typeof values === 'object' ? values : {})
|
||||||
|
};
|
||||||
|
const res = await createBackend({ data });
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
onNext(submit);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
navigate(-1);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContainer
|
||||||
|
ghost
|
||||||
|
footer={[
|
||||||
|
<FooterButtons
|
||||||
|
key="buttons"
|
||||||
|
onPrevious={onPrevious}
|
||||||
|
onNext={onNext}
|
||||||
|
handleCancel={handleCancel}
|
||||||
|
handleSubmit={handleSubmit}
|
||||||
|
showButtons={showButtons}
|
||||||
|
/>
|
||||||
|
]}
|
||||||
|
header={{
|
||||||
|
title: false,
|
||||||
|
style: {
|
||||||
|
paddingInline: 'var(--layout-content-header-inlinepadding)'
|
||||||
|
},
|
||||||
|
breadcrumb: {}
|
||||||
|
}}
|
||||||
|
pageHeaderRender={() => (
|
||||||
|
<HeaderContainer>
|
||||||
|
<Nav>
|
||||||
|
<span className="level-1">Backends</span>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
marginInline: 20,
|
||||||
|
color: 'var(--ant-color-split)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
/
|
||||||
|
</span>
|
||||||
|
<span className="level-2">
|
||||||
|
{intl.formatMessage({ id: 'common.button.create' })}
|
||||||
|
</span>
|
||||||
|
</Nav>
|
||||||
|
<ClusterSteps
|
||||||
|
steps={steps}
|
||||||
|
currentStep={currentStep}
|
||||||
|
onChange={handleStepChange}
|
||||||
|
></ClusterSteps>
|
||||||
|
</HeaderContainer>
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Container>
|
||||||
|
<Content>{renderForms()}</Content>
|
||||||
|
</Container>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ClusterCreate;
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
import IconFont from '@/components/icon-font';
|
||||||
|
import ModalFooter from '@/components/modal-footer';
|
||||||
|
import GSDrawer from '@/components/scroller-modal/gs-drawer';
|
||||||
|
import { PageActionType } from '@/config/types';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Segmented, Tabs } from 'antd';
|
||||||
|
import React, { useRef, useState } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import ColumnWrapper from '../../_components/column-wrapper';
|
||||||
|
import { FormData, ListItem } from '../config/types';
|
||||||
|
import BackendForm from '../forms';
|
||||||
|
import ImportYAML from './import-yaml';
|
||||||
|
|
||||||
|
const ModalFooterStyle = {
|
||||||
|
padding: '16px 24px',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-end'
|
||||||
|
};
|
||||||
|
|
||||||
|
const SegmentedInner = styled(Segmented)`
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 0;
|
||||||
|
.ant-segmented-item {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SegmentedHeader = styled.div`
|
||||||
|
margin-top: 16px;
|
||||||
|
padding: 0 24px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface AddModalProps {
|
||||||
|
action: PageActionType;
|
||||||
|
currentData?: ListItem; // Used when action is EDIT
|
||||||
|
onClose: () => void;
|
||||||
|
onSubmit: (values: FormData) => void;
|
||||||
|
open: boolean;
|
||||||
|
title?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const AddModal: React.FC<AddModalProps> = (props) => {
|
||||||
|
const { action, currentData, onClose, onSubmit, open, title } = props;
|
||||||
|
const formRef = useRef<any>(null);
|
||||||
|
const editorRef = useRef<any>(null);
|
||||||
|
const intl = useIntl();
|
||||||
|
const [activeKey, setActiveKey] = useState<string>('form');
|
||||||
|
|
||||||
|
const onOk = () => {
|
||||||
|
if (activeKey === 'yaml') {
|
||||||
|
const content = editorRef.current?.getContent();
|
||||||
|
const valid = editorRef.current?.validate();
|
||||||
|
if (valid) {
|
||||||
|
onSubmit({ content: content });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
formRef.current?.submit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFinish = (values: FormData) => {
|
||||||
|
onSubmit(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<GSDrawer
|
||||||
|
title={title}
|
||||||
|
open={open}
|
||||||
|
onClose={onClose}
|
||||||
|
destroyOnHidden={true}
|
||||||
|
closeIcon={false}
|
||||||
|
maskClosable={false}
|
||||||
|
keyboard={false}
|
||||||
|
styles={{
|
||||||
|
body: {
|
||||||
|
padding: '0 0 16px',
|
||||||
|
overflowX: 'hidden'
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
borderRadius: '6px 0 0 6px'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
width={600}
|
||||||
|
footer={false}
|
||||||
|
>
|
||||||
|
<SegmentedHeader>
|
||||||
|
<SegmentedInner
|
||||||
|
shape="round"
|
||||||
|
onChange={(value) => setActiveKey(value as string)}
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
label: 'Form',
|
||||||
|
value: 'form',
|
||||||
|
icon: <IconFont type="icon-edit-content" />
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'YAML',
|
||||||
|
value: 'yaml',
|
||||||
|
icon: <IconFont type="icon-code_block" />
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
size="middle"
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
/>
|
||||||
|
</SegmentedHeader>
|
||||||
|
<ColumnWrapper
|
||||||
|
style={{ flex: 1 }}
|
||||||
|
maxHeight={'calc(100vh - 123px)'}
|
||||||
|
footer={
|
||||||
|
<ModalFooter
|
||||||
|
onCancel={onClose}
|
||||||
|
onOk={onOk}
|
||||||
|
style={ModalFooterStyle}
|
||||||
|
></ModalFooter>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Tabs
|
||||||
|
renderTabBar={() => <></>}
|
||||||
|
activeKey={activeKey}
|
||||||
|
defaultActiveKey={activeKey}
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: 'form',
|
||||||
|
label: 'Form',
|
||||||
|
children: (
|
||||||
|
<BackendForm
|
||||||
|
onFinish={onFinish}
|
||||||
|
action={action}
|
||||||
|
currentData={currentData}
|
||||||
|
ref={formRef}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'yaml',
|
||||||
|
label: 'YAML',
|
||||||
|
children: (
|
||||||
|
<ImportYAML action={action} ref={editorRef}></ImportYAML>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
></Tabs>
|
||||||
|
</ColumnWrapper>
|
||||||
|
</GSDrawer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AddModal;
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import DropDownActions from '@/components/drop-down-actions';
|
import DropDownActions from '@/components/drop-down-actions';
|
||||||
import IconFont from '@/components/icon-font';
|
import IconFont from '@/components/icon-font';
|
||||||
import Card from '@/components/templates/card';
|
import Card from '@/components/templates/card';
|
||||||
|
import { DockerOutlined } from '@ant-design/icons';
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import { backendActions } from '../config';
|
import { backendActions } from '../config';
|
||||||
@@ -22,8 +23,17 @@ const Content = styled.div`
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
margin-top: 12x;
|
margin-top: 8px;
|
||||||
gap: 16px;
|
gap: 12px;
|
||||||
|
color: var(--ant-color-text-secondary);
|
||||||
|
.description {
|
||||||
|
margin-block: 12px;
|
||||||
|
color: var(--ant-color-text-tertiary);
|
||||||
|
}
|
||||||
|
.text {
|
||||||
|
margin-left: 4px;
|
||||||
|
color: var(--ant-color-text-secondary);
|
||||||
|
}
|
||||||
.title {
|
.title {
|
||||||
color: var(--ant-color-text);
|
color: var(--ant-color-text);
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@@ -47,12 +57,13 @@ const BackendCard: React.FC<BackendCardProps> = ({
|
|||||||
onClick={() => onClick(data)}
|
onClick={() => onClick(data)}
|
||||||
clickable={true}
|
clickable={true}
|
||||||
disabled={false}
|
disabled={false}
|
||||||
height={205}
|
height={'auto'}
|
||||||
ghost
|
ghost
|
||||||
header={
|
header={
|
||||||
<Header>
|
<Header>
|
||||||
<div className="title">
|
<div className="title">
|
||||||
<img src={data.icon} alt={data.backend_name} height={20} />
|
<img src={data.icon} alt={data.backend_name} height={20} />
|
||||||
|
<span>{data.backend_name}</span>
|
||||||
</div>
|
</div>
|
||||||
<span>
|
<span>
|
||||||
<DropDownActions
|
<DropDownActions
|
||||||
@@ -72,11 +83,16 @@ const BackendCard: React.FC<BackendCardProps> = ({
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Content>
|
<Content>
|
||||||
<div className="title">{data.backend_name}</div>
|
<div className="description">This is a description</div>
|
||||||
<div>This is a description</div>
|
|
||||||
<div>默认版本:{data.default_version}</div>
|
|
||||||
<div>
|
<div>
|
||||||
镜像:{data.version_configs?.[data.default_version]?.image_name}
|
<IconFont type="icon-version" style={{ marginRight: 4 }} /> Default
|
||||||
|
version: <span className="text">{data.default_version}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<DockerOutlined style={{ marginRight: 4 }} /> Default Image:{' '}
|
||||||
|
<span className="text">
|
||||||
|
{data.version_configs?.[data.default_version]?.image_name}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Content>
|
</Content>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
import { PageAction } from '@/config';
|
||||||
|
import { PageActionType } from '@/config/types';
|
||||||
|
import useUserSettings from '@/hooks/use-user-settings';
|
||||||
|
import { ImportOutlined } from '@ant-design/icons';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Button, message, Typography, Upload } from 'antd';
|
||||||
|
import { RcFile } from 'antd/lib/upload';
|
||||||
|
import React, {
|
||||||
|
forwardRef,
|
||||||
|
useImperativeHandle,
|
||||||
|
useRef,
|
||||||
|
useState
|
||||||
|
} from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { yamlTemplate } from '../config';
|
||||||
|
import YamlEditor from './yaml-editor';
|
||||||
|
|
||||||
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
const ErrorText = styled(Text)`
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
padding: 4px 6px;
|
||||||
|
background-color: var(--ant-color-bg-elevated);
|
||||||
|
border-radius: 0 0 var(--ant-border-radius) var(--ant-border-radius);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Header = styled.div`
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 40px;
|
||||||
|
padding-inline: 10px;
|
||||||
|
border-bottom: 1px solid var(--ant-color-border);
|
||||||
|
background-color: var(--ant-color-fill-tertiary);
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Container = styled.div`
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid var(--ant-color-border);
|
||||||
|
border-radius: var(--ant-border-radius);
|
||||||
|
.monaco-editor .scroll-decoration {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
interface ImportYAMLProps {
|
||||||
|
ref?: any;
|
||||||
|
action: PageActionType;
|
||||||
|
content?: string;
|
||||||
|
onSubmit?: (content: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImportYAML: React.FC<ImportYAMLProps> = forwardRef(
|
||||||
|
({ action, content = '' }, ref) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const { userSettings } = useUserSettings();
|
||||||
|
const editorRef = useRef<any>(null);
|
||||||
|
const [fileContent, setFileContent] = useState<string>(
|
||||||
|
action === PageAction.CREATE ? yamlTemplate : content
|
||||||
|
);
|
||||||
|
const [error, setError] = useState<string>('');
|
||||||
|
|
||||||
|
const beforeUpload = (file: RcFile) => {
|
||||||
|
const isYaml =
|
||||||
|
file.type === 'application/x-yaml' ||
|
||||||
|
file.type === 'text/yaml' ||
|
||||||
|
file.name.endsWith('.yaml') ||
|
||||||
|
file.name.endsWith('.yml');
|
||||||
|
if (!isYaml) {
|
||||||
|
message.error('You can only upload YAML file!');
|
||||||
|
}
|
||||||
|
const reader = new FileReader();
|
||||||
|
reader.onload = (e) => {
|
||||||
|
const content = e.target?.result;
|
||||||
|
if (typeof content === 'string') {
|
||||||
|
setFileContent(content);
|
||||||
|
} else {
|
||||||
|
message.error('Failed to read file content!');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsText(file);
|
||||||
|
// Prevent upload
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const validate = () => {
|
||||||
|
const markers = editorRef.current?.validate();
|
||||||
|
if (markers && markers.length > 0) {
|
||||||
|
setError(markers[0].message);
|
||||||
|
}
|
||||||
|
return markers.length === 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderHeader = () => {
|
||||||
|
return (
|
||||||
|
<Header>
|
||||||
|
<span className="title">YAML Editor</span>
|
||||||
|
<Upload
|
||||||
|
name="file"
|
||||||
|
multiple={false}
|
||||||
|
beforeUpload={beforeUpload}
|
||||||
|
showUploadList={false}
|
||||||
|
accept=".yaml,.yml,text/yaml,application/x-yaml"
|
||||||
|
>
|
||||||
|
<Button icon={<ImportOutlined />} type="text" size="small">
|
||||||
|
Import
|
||||||
|
</Button>
|
||||||
|
</Upload>
|
||||||
|
</Header>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
getContent: () => {
|
||||||
|
return editorRef.current?.getValue();
|
||||||
|
},
|
||||||
|
setContent: (val: string) => {
|
||||||
|
editorRef.current?.setValue?.(val);
|
||||||
|
},
|
||||||
|
validate() {
|
||||||
|
return validate();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<YamlEditor
|
||||||
|
ref={editorRef}
|
||||||
|
lang="yaml"
|
||||||
|
theme={userSettings?.isDarkTheme ? 'vs-dark' : 'vs-light'}
|
||||||
|
value={fileContent}
|
||||||
|
height={'calc(100vh - 255px)'}
|
||||||
|
header={renderHeader()}
|
||||||
|
></YamlEditor>
|
||||||
|
{error && <ErrorText type="danger">{error}</ErrorText>}
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default ImportYAML;
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
import EditorWrap from '@/components/editor-wrap';
|
||||||
|
import { LoadingOutlined } from '@ant-design/icons';
|
||||||
|
import Editor, { loader } from '@monaco-editor/react';
|
||||||
|
import * as monaco from 'monaco-editor';
|
||||||
|
import { configureMonacoYaml } from 'monaco-yaml';
|
||||||
|
import React, {
|
||||||
|
forwardRef,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useRef
|
||||||
|
} from 'react';
|
||||||
|
import schema from '../config/schema.json';
|
||||||
|
|
||||||
|
loader.config({ monaco });
|
||||||
|
|
||||||
|
interface ViewerProps {
|
||||||
|
ref?: any;
|
||||||
|
lang: string;
|
||||||
|
defaultLang?: string;
|
||||||
|
config?: any;
|
||||||
|
value: string;
|
||||||
|
height?: string | number;
|
||||||
|
theme?: string;
|
||||||
|
header?: React.ReactNode;
|
||||||
|
placeholder?: string;
|
||||||
|
variant?: 'bordered' | 'borderless';
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = 'inmemory://model/config.yaml';
|
||||||
|
|
||||||
|
const ViewerEditor: React.FC<ViewerProps> = forwardRef((props, ref) => {
|
||||||
|
const {
|
||||||
|
lang,
|
||||||
|
value,
|
||||||
|
config,
|
||||||
|
defaultLang,
|
||||||
|
height = 380,
|
||||||
|
theme = 'vs-dark',
|
||||||
|
header,
|
||||||
|
variant = 'borderless',
|
||||||
|
placeholder
|
||||||
|
} = props;
|
||||||
|
|
||||||
|
const editorRef = useRef<any>(null);
|
||||||
|
const monacoRef = useRef<any>(null);
|
||||||
|
const monacoYamlRef = useRef<any>(null);
|
||||||
|
|
||||||
|
const handleBeforeMount = (monaco: any) => {
|
||||||
|
const yamlUri = monaco.Uri.parse(path);
|
||||||
|
|
||||||
|
configureMonacoYaml(monaco, {
|
||||||
|
schemas: [
|
||||||
|
{
|
||||||
|
uri: 'http://example.com/schema-name.json',
|
||||||
|
fileMatch: [yamlUri.toString()],
|
||||||
|
schema
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEditorDidMount = (editor: any, monaco: any) => {
|
||||||
|
editorRef.current = editor;
|
||||||
|
monacoRef.current = monaco;
|
||||||
|
const uri = monaco.Uri.parse(path);
|
||||||
|
|
||||||
|
// monaco.editor.onDidChangeMarkers((uris: string[]) => {
|
||||||
|
// if (!uris.some((u) => u.toString() === uri.toString())) return;
|
||||||
|
|
||||||
|
// const model = monaco.editor.getModel(uri);
|
||||||
|
// if (!model) return;
|
||||||
|
// console.log('Markers changed:', uris);
|
||||||
|
// const markers = monaco.editor.getModelMarkers({ resource: uri });
|
||||||
|
// const adjusted = markers.map((m: any) => {
|
||||||
|
// if (m._adjusted) return m;
|
||||||
|
// return {
|
||||||
|
// ...m,
|
||||||
|
// _adjusted: true,
|
||||||
|
// severity: monaco.MarkerSeverity.ErrorText
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
|
||||||
|
// monaco.editor.setModelMarkers(
|
||||||
|
// monaco.editor.getModel(uri),
|
||||||
|
// 'yaml',
|
||||||
|
// adjusted
|
||||||
|
// );
|
||||||
|
// });
|
||||||
|
};
|
||||||
|
|
||||||
|
const formatCode = () => {
|
||||||
|
if (editorRef.current) {
|
||||||
|
setTimeout(() => {
|
||||||
|
editorRef.current
|
||||||
|
?.getAction?.('editor.action.formatDocument')
|
||||||
|
?.run()
|
||||||
|
.then(() => {
|
||||||
|
console.log('format success');
|
||||||
|
});
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMarkers = () => {
|
||||||
|
const uri = editorRef.current?.getModel()?.uri;
|
||||||
|
const markers = monacoRef.current?.editor.getModelMarkers({
|
||||||
|
resource: uri
|
||||||
|
});
|
||||||
|
console.log('Validation markers:', uri, markers);
|
||||||
|
return markers;
|
||||||
|
};
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
format: () => {
|
||||||
|
formatCode();
|
||||||
|
},
|
||||||
|
getValue: () => {
|
||||||
|
return editorRef.current?.getValue?.();
|
||||||
|
},
|
||||||
|
setValue: (val: string) => {
|
||||||
|
editorRef.current?.setValue?.(val);
|
||||||
|
},
|
||||||
|
dispose: () => {
|
||||||
|
editorRef.current?.dispose?.();
|
||||||
|
monacoYamlRef.current?.dispose?.();
|
||||||
|
},
|
||||||
|
validate() {
|
||||||
|
return getMarkers();
|
||||||
|
},
|
||||||
|
editor: editorRef.current
|
||||||
|
}));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
formatCode();
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<EditorWrap header={header} variant={variant}>
|
||||||
|
<Editor
|
||||||
|
path={path}
|
||||||
|
defaultPath={path}
|
||||||
|
height={height}
|
||||||
|
theme={theme}
|
||||||
|
className="monaco-editor"
|
||||||
|
defaultLanguage={'yaml'}
|
||||||
|
language={'yaml'}
|
||||||
|
value={value}
|
||||||
|
options={{
|
||||||
|
minimap: { enabled: false },
|
||||||
|
quickSuggestions: true,
|
||||||
|
suggestOnTriggerCharacters: true,
|
||||||
|
fontSize: 14,
|
||||||
|
scrollbar: {
|
||||||
|
verticalScrollbarSize: 6,
|
||||||
|
horizontalScrollbarSize: 6
|
||||||
|
},
|
||||||
|
placeholder: placeholder
|
||||||
|
}}
|
||||||
|
loading={<LoadingOutlined style={{ fontSize: 24 }}></LoadingOutlined>}
|
||||||
|
beforeMount={handleBeforeMount}
|
||||||
|
onMount={handleEditorDidMount}
|
||||||
|
/>
|
||||||
|
</EditorWrap>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ViewerEditor;
|
||||||
@@ -5,21 +5,21 @@ export const backendActions = [
|
|||||||
label: 'Edit Versions',
|
label: 'Edit Versions',
|
||||||
value: 'version',
|
value: 'version',
|
||||||
key: 'version',
|
key: 'version',
|
||||||
icon: icons.EditOutlined,
|
icon: icons.EditContent,
|
||||||
locale: false
|
locale: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Edit Parameters',
|
label: 'Edit Parameters',
|
||||||
value: 'parameter',
|
value: 'parameter',
|
||||||
key: 'parameter',
|
key: 'parameter',
|
||||||
icon: icons.EditOutlined,
|
icon: icons.Parameter,
|
||||||
locale: false
|
locale: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Edit by Yaml',
|
label: 'Edit by Yaml',
|
||||||
value: 'yaml',
|
value: 'yaml',
|
||||||
key: 'edit',
|
key: 'edit',
|
||||||
icon: icons.EditOutlined,
|
icon: icons.Yaml,
|
||||||
locale: false
|
locale: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -31,3 +31,21 @@ export const backendActions = [
|
|||||||
danger: true
|
danger: true
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const yamlTemplate = `
|
||||||
|
name: SGLang
|
||||||
|
default_version: v0.5.1
|
||||||
|
health_check_path: /health
|
||||||
|
allowed_proxy_uris:
|
||||||
|
- api/chat
|
||||||
|
- api/v1
|
||||||
|
versions:
|
||||||
|
v0.0.1:
|
||||||
|
image: lm/sglang
|
||||||
|
command: run sglang
|
||||||
|
v0.0.2:
|
||||||
|
image: lm/sglang
|
||||||
|
command: run sglang
|
||||||
|
default_backend_parameters:
|
||||||
|
- --host
|
||||||
|
`;
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "backend name",
|
||||||
|
"severity": "error"
|
||||||
|
},
|
||||||
|
"default_version": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "default backend version"
|
||||||
|
},
|
||||||
|
"health_check_path": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "health check path"
|
||||||
|
},
|
||||||
|
"allowed_proxy_uris": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "allowed proxy URIs"
|
||||||
|
},
|
||||||
|
"versions": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-zA-Z0-9._-]+$": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"image": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "image name"
|
||||||
|
},
|
||||||
|
"command": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "start command"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["image", "command"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"description": "multiple backend versions"
|
||||||
|
},
|
||||||
|
"default_backend_parameters": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": "default backend parameters"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["name", "default_version", "health_check_path", "versions"],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import nvidiaLogo from '@/assets/logo/nvidia.png';
|
import lmDeployLogo from '@/assets/logo/lmdeploy.png';
|
||||||
import SGLangLogo from '@/assets/logo/sglang.png';
|
import SGLangLogo from '@/assets/logo/sglang.png';
|
||||||
import vLLMLogo from '@/assets/logo/vllm.png';
|
import vLLMLogo from '@/assets/logo/vllm.png';
|
||||||
|
|
||||||
@@ -37,8 +37,8 @@ export default [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
backend_name: 'TensorRT',
|
backend_name: 'LMDeploy',
|
||||||
icon: nvidiaLogo,
|
icon: lmDeployLogo,
|
||||||
version_configs: {
|
version_configs: {
|
||||||
'v1.0': {
|
'v1.0': {
|
||||||
image_name: 'example/image:v1.0',
|
image_name: 'example/image:v1.0',
|
||||||
@@ -49,6 +49,6 @@ export default [
|
|||||||
health_check_path: '/v1/models',
|
health_check_path: '/v1/models',
|
||||||
default_version: 'v1.0',
|
default_version: 'v1.0',
|
||||||
default_backend_param: ['--param1', '--param2'],
|
default_backend_param: ['--param1', '--param2'],
|
||||||
description: 'TensorRT description'
|
description: 'LMDeploy description'
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export interface VersionConfigs {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface FormData {
|
export interface FormData {
|
||||||
backend_name: string;
|
backend_name?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
version_configs?: VersionConfigs;
|
version_configs?: VersionConfigs;
|
||||||
compatibility_type?: string;
|
compatibility_type?: string;
|
||||||
@@ -14,10 +14,11 @@ export interface FormData {
|
|||||||
default_version?: string;
|
default_version?: string;
|
||||||
default_backend_param?: string[];
|
default_backend_param?: string[];
|
||||||
allowed_proxy_uris?: string[];
|
allowed_proxy_uris?: string[];
|
||||||
|
content?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ListItem extends FormData {
|
export interface ListItem extends FormData {
|
||||||
id: number;
|
id?: number;
|
||||||
created_at: string;
|
created_at?: string;
|
||||||
updated_at: string;
|
updated_at?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import ListInput from '@/components/list-input';
|
||||||
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
|
import SealSelect from '@/components/seal-form/seal-select';
|
||||||
|
import { PageActionType } from '@/config/types';
|
||||||
|
import useAppUtils from '@/hooks/use-app-utils';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Form } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
import { FormData } from '../config/types';
|
||||||
|
|
||||||
|
type AddModalProps = {
|
||||||
|
action: PageActionType;
|
||||||
|
};
|
||||||
|
const BasicForm: React.FC<AddModalProps> = ({ action }) => {
|
||||||
|
const form = Form.useFormInstance();
|
||||||
|
const intl = useIntl();
|
||||||
|
const { getRuleMessage } = useAppUtils();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item<FormData>
|
||||||
|
name="backend_name"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: getRuleMessage('input', 'common.table.name')
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<SealInput.Input
|
||||||
|
label={intl.formatMessage({ id: 'common.table.name' })}
|
||||||
|
required
|
||||||
|
></SealInput.Input>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item<FormData>
|
||||||
|
name="compatibility_type"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: getRuleMessage('select', 'template', false)
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<SealSelect
|
||||||
|
label={'Compatibility Template'}
|
||||||
|
required
|
||||||
|
options={[
|
||||||
|
{ label: 'Template 1', value: 'tensorflow' },
|
||||||
|
{ label: 'Template 2', value: 'torchserve' }
|
||||||
|
]}
|
||||||
|
></SealSelect>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item<FormData>
|
||||||
|
name="allowed_proxy_uris"
|
||||||
|
rules={[{ required: false }]}
|
||||||
|
>
|
||||||
|
<ListInput
|
||||||
|
dataList={form.getFieldValue('allowed_proxy_uris') || []}
|
||||||
|
onChange={(data: string[]) => {
|
||||||
|
form.setFieldValue('allowed_proxy_uris', data);
|
||||||
|
}}
|
||||||
|
btnText={'Add Allowed Proxy URI'}
|
||||||
|
label={'Proxy URI'}
|
||||||
|
></ListInput>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item<FormData> name="description" rules={[{ required: false }]}>
|
||||||
|
<SealInput.TextArea
|
||||||
|
label={intl.formatMessage({ id: 'common.table.description' })}
|
||||||
|
></SealInput.TextArea>
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default BasicForm;
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { PageActionType } from '@/config/types';
|
||||||
|
import CollapsePanel from '@/pages/_components/collapse-panel';
|
||||||
|
import { Form } from 'antd';
|
||||||
|
import React, { forwardRef, useEffect, useImperativeHandle } from 'react';
|
||||||
|
import { FormData, ListItem } from '../config/types';
|
||||||
|
import BasicForm from './basic';
|
||||||
|
import ParametersForm from './parameters';
|
||||||
|
import VersionsForm from './versions-config';
|
||||||
|
|
||||||
|
type AddModalProps = {
|
||||||
|
action: PageActionType;
|
||||||
|
currentData?: ListItem; // Used when action is EDIT
|
||||||
|
onFinish: (values: FormData) => void;
|
||||||
|
ref?: any;
|
||||||
|
};
|
||||||
|
const BackendForm: React.FC<AddModalProps> = forwardRef(
|
||||||
|
({ action, currentData, onFinish }, ref) => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentData) {
|
||||||
|
form.setFieldsValue(currentData);
|
||||||
|
}
|
||||||
|
}, [currentData]);
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
resetFields: () => {
|
||||||
|
form.resetFields();
|
||||||
|
},
|
||||||
|
submit: () => {
|
||||||
|
form.submit();
|
||||||
|
},
|
||||||
|
setFieldsValue: (values: any) => {
|
||||||
|
form.setFieldsValue(values);
|
||||||
|
},
|
||||||
|
getFieldsValue: () => {
|
||||||
|
return form.getFieldsValue();
|
||||||
|
},
|
||||||
|
validateFields: async () => {
|
||||||
|
return await form.validateFields();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form
|
||||||
|
name="basicForm"
|
||||||
|
form={form}
|
||||||
|
onFinish={onFinish}
|
||||||
|
preserve={false}
|
||||||
|
scrollToFirstError={true}
|
||||||
|
initialValues={currentData}
|
||||||
|
>
|
||||||
|
<BasicForm action={action}></BasicForm>
|
||||||
|
<CollapsePanel
|
||||||
|
items={[
|
||||||
|
{
|
||||||
|
key: 'versions',
|
||||||
|
label: 'Versions',
|
||||||
|
children: <VersionsForm action={action}></VersionsForm>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'parameters',
|
||||||
|
label: 'Default Parameters',
|
||||||
|
children: <ParametersForm action={action}></ParametersForm>
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
></CollapsePanel>
|
||||||
|
</Form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default BackendForm;
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import ListInput from '@/components/list-input';
|
||||||
|
import { PageActionType } from '@/config/types';
|
||||||
|
import { Form } from 'antd';
|
||||||
|
import React from 'react';
|
||||||
|
import { FormData } from '../config/types';
|
||||||
|
|
||||||
|
type AddModalProps = {
|
||||||
|
action: PageActionType;
|
||||||
|
};
|
||||||
|
const ParametersForm: React.FC<AddModalProps> = ({ action }) => {
|
||||||
|
const form = Form.useFormInstance();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Form.Item<FormData>
|
||||||
|
name="default_backend_param"
|
||||||
|
rules={[{ required: false }]}
|
||||||
|
>
|
||||||
|
<ListInput
|
||||||
|
dataList={form.getFieldValue('default_backend_param') || []}
|
||||||
|
onChange={(data: string[]) => {
|
||||||
|
form.setFieldValue('default_backend_param', data);
|
||||||
|
}}
|
||||||
|
btnText={'Add Backend Parameter'}
|
||||||
|
label={'Default Backend Parameters'}
|
||||||
|
></ListInput>
|
||||||
|
</Form.Item>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ParametersForm;
|
||||||
@@ -0,0 +1,139 @@
|
|||||||
|
import SealInput from '@/components/seal-form/seal-input';
|
||||||
|
import { PageActionType } from '@/config/types';
|
||||||
|
import { MinusOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Checkbox, Divider, Form } from 'antd';
|
||||||
|
import React, { useEffect } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Box = styled.div`
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 200px 1fr;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ItemWrapper = styled.div`
|
||||||
|
&:hover {
|
||||||
|
.divider {
|
||||||
|
border-top: 1px dashed var(--ant-color-primary) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Label = styled.div`
|
||||||
|
color: var(--ant-color-text-tertiary);
|
||||||
|
font-size: var(--font-size-base);
|
||||||
|
margin-bottom: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
type AddModalProps = {
|
||||||
|
action: PageActionType;
|
||||||
|
};
|
||||||
|
const VersionsForm: React.FC<AddModalProps> = ({ action }) => {
|
||||||
|
const form = Form.useFormInstance();
|
||||||
|
const versionList = [
|
||||||
|
{
|
||||||
|
version_no: '',
|
||||||
|
image_name: '',
|
||||||
|
run_command: '',
|
||||||
|
is_default: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const versions = form.getFieldValue('version_configs') || [];
|
||||||
|
if (Array.isArray(versions) && versions.length === 0) {
|
||||||
|
form.setFieldValue('version_configs', versionList);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Form.List name="version_configs">
|
||||||
|
{(fields, { add, remove }) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
border: '1px solid var(--ant-color-border)',
|
||||||
|
borderRadius: 'var(--border-radius-base)',
|
||||||
|
padding: '14px'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{fields.map(({ key, name, ...restField }, index) => (
|
||||||
|
<ItemWrapper key={key}>
|
||||||
|
<Box>
|
||||||
|
<Form.Item
|
||||||
|
{...restField}
|
||||||
|
name={[name, 'version_no']}
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: 'Version No. is required' }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<SealInput.Input
|
||||||
|
label="Version No."
|
||||||
|
required
|
||||||
|
></SealInput.Input>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
{...restField}
|
||||||
|
name={[name, 'image_name']}
|
||||||
|
rules={[
|
||||||
|
{ required: true, message: 'Image Name is required' }
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<SealInput.Input
|
||||||
|
label="Image Name"
|
||||||
|
required
|
||||||
|
></SealInput.Input>
|
||||||
|
</Form.Item>
|
||||||
|
</Box>
|
||||||
|
<Form.Item name={[name, 'run_command']} {...restField}>
|
||||||
|
<SealInput.TextArea label="Execution Command"></SealInput.TextArea>
|
||||||
|
</Form.Item>
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<span className="flex-center">
|
||||||
|
<Form.Item
|
||||||
|
{...restField}
|
||||||
|
name={[name, 'is_default']}
|
||||||
|
valuePropName="checked"
|
||||||
|
initialValue={false}
|
||||||
|
noStyle
|
||||||
|
>
|
||||||
|
<Checkbox>Set as Default Version</Checkbox>
|
||||||
|
</Form.Item>
|
||||||
|
</span>
|
||||||
|
<div className="flex-center gap-16">
|
||||||
|
{fields.length > 1 && (
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
onClick={() => remove(name)}
|
||||||
|
shape="circle"
|
||||||
|
>
|
||||||
|
<MinusOutlined />
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Divider
|
||||||
|
className="divider"
|
||||||
|
style={{ borderTop: '1px dashed var(--ant-color-border)' }}
|
||||||
|
/>
|
||||||
|
{index === fields.length - 1 && (
|
||||||
|
<Button
|
||||||
|
onClick={() => add()}
|
||||||
|
block
|
||||||
|
variant="filled"
|
||||||
|
color="default"
|
||||||
|
>
|
||||||
|
<PlusOutlined /> Add Version
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</ItemWrapper>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</Form.List>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VersionsForm;
|
||||||
+38
-38
@@ -1,21 +1,18 @@
|
|||||||
import { FilterBar } from '@/components/page-tools';
|
import { FilterBar } from '@/components/page-tools';
|
||||||
|
import { PageActionType } from '@/config/types';
|
||||||
import useTableFetch from '@/hooks/use-table-fetch';
|
import useTableFetch from '@/hooks/use-table-fetch';
|
||||||
import {
|
|
||||||
DeleteOutlined,
|
|
||||||
ImportOutlined,
|
|
||||||
PlusOutlined
|
|
||||||
} from '@ant-design/icons';
|
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Space } from 'antd';
|
import { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
deleteBackend,
|
deleteBackend,
|
||||||
INFERENCE_BACKEND_API,
|
INFERENCE_BACKEND_API,
|
||||||
queryBackendsList
|
queryBackendsList
|
||||||
} from './apis';
|
} from './apis';
|
||||||
|
import AddModal from './components/add-modal';
|
||||||
import BackendCardList from './components/backend-list';
|
import BackendCardList from './components/backend-list';
|
||||||
import dataList from './config/test';
|
import dataList from './config/test';
|
||||||
import { ListItem } from './config/types';
|
import { FormData, ListItem } from './config/types';
|
||||||
|
|
||||||
const BackendList = () => {
|
const BackendList = () => {
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
@@ -40,10 +37,27 @@ const BackendList = () => {
|
|||||||
watch: false,
|
watch: false,
|
||||||
contentForDelete: 'resources.modelfiles.modelfile'
|
contentForDelete: 'resources.modelfiles.modelfile'
|
||||||
});
|
});
|
||||||
|
const [openModalStatus, setOpenModalStatus] = useState<{
|
||||||
|
open: boolean;
|
||||||
|
action: PageActionType;
|
||||||
|
currentData?: Partial<FormData>;
|
||||||
|
}>({ open: false, action: 'create' });
|
||||||
|
|
||||||
const handleClickDropdown = () => {};
|
const handleOnSubmit = (values: FormData) => {
|
||||||
|
console.log('yaml content:', values);
|
||||||
|
setOpenModalStatus({
|
||||||
|
open: false,
|
||||||
|
action: 'create',
|
||||||
|
currentData: undefined
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleDeleteByBatch = () => {};
|
const handleAddBackend = () => {
|
||||||
|
setOpenModalStatus({
|
||||||
|
open: true,
|
||||||
|
action: 'create'
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer
|
<PageContainer
|
||||||
@@ -63,40 +77,14 @@ const BackendList = () => {
|
|||||||
width={{
|
width={{
|
||||||
input: 300
|
input: 300
|
||||||
}}
|
}}
|
||||||
actionType="dropdown"
|
|
||||||
inputHolder={intl.formatMessage({ id: 'common.filter.name' })}
|
inputHolder={intl.formatMessage({ id: 'common.filter.name' })}
|
||||||
buttonText={intl.formatMessage({
|
buttonText={'Add Backend'}
|
||||||
id: 'resources.modelfiles.download'
|
handleDeleteByBatch={handleDeleteBatch}
|
||||||
})}
|
handleClickPrimary={handleAddBackend}
|
||||||
handleDeleteByBatch={handleDeleteByBatch}
|
|
||||||
handleClickPrimary={handleClickDropdown}
|
|
||||||
handleSearch={handleSearch}
|
handleSearch={handleSearch}
|
||||||
handleInputChange={handleNameChange}
|
handleInputChange={handleNameChange}
|
||||||
rowSelection={rowSelection}
|
rowSelection={rowSelection}
|
||||||
showSelect={false}
|
showSelect={false}
|
||||||
right={
|
|
||||||
<Space size={20}>
|
|
||||||
<Button type="primary" icon={<PlusOutlined />}>
|
|
||||||
Add Backend
|
|
||||||
</Button>
|
|
||||||
<Button type="primary" icon={<ImportOutlined />}>
|
|
||||||
Import backend from yaml
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
icon={<DeleteOutlined />}
|
|
||||||
danger
|
|
||||||
onClick={handleDeleteByBatch}
|
|
||||||
disabled={!rowSelection?.selectedRowKeys?.length}
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
{intl?.formatMessage?.({ id: 'common.button.delete' })}
|
|
||||||
{rowSelection?.selectedRowKeys?.length > 0 && (
|
|
||||||
<span>({rowSelection?.selectedRowKeys?.length})</span>
|
|
||||||
)}
|
|
||||||
</span>
|
|
||||||
</Button>
|
|
||||||
</Space>
|
|
||||||
}
|
|
||||||
></FilterBar>
|
></FilterBar>
|
||||||
|
|
||||||
<BackendCardList
|
<BackendCardList
|
||||||
@@ -105,6 +93,18 @@ const BackendList = () => {
|
|||||||
activeId={false}
|
activeId={false}
|
||||||
isFirst={!dataSource.loadend}
|
isFirst={!dataSource.loadend}
|
||||||
></BackendCardList>
|
></BackendCardList>
|
||||||
|
<AddModal
|
||||||
|
action={openModalStatus.action}
|
||||||
|
onClose={() => setOpenModalStatus({ open: false, action: 'create' })}
|
||||||
|
onSubmit={handleOnSubmit}
|
||||||
|
currentData={openModalStatus.currentData}
|
||||||
|
open={openModalStatus.open}
|
||||||
|
title={
|
||||||
|
openModalStatus.action === 'create'
|
||||||
|
? 'Create Backend'
|
||||||
|
: 'Edit Backend'
|
||||||
|
}
|
||||||
|
></AddModal>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import PageTools from '@/components/page-tools';
|
||||||
|
import { PageActionType } from '@/config/types';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { forwardRef, useImperativeHandle, useRef } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import Basic from '../forms/basic';
|
||||||
|
|
||||||
|
const Title = styled.span`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 16px;
|
||||||
|
.text {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
interface BasicFormProps {
|
||||||
|
action: PageActionType;
|
||||||
|
currentData?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BasicForm = forwardRef((props: BasicFormProps, ref) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const { action, currentData } = props;
|
||||||
|
const formRef = useRef<any>(null);
|
||||||
|
|
||||||
|
const handleOnFinish = (values: any) => {
|
||||||
|
console.log(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
validateFields: formRef.current?.validateFields,
|
||||||
|
getFieldsValue: formRef.current?.getFieldsValue,
|
||||||
|
submit: formRef.current?.submit
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PageTools
|
||||||
|
marginBottom={16}
|
||||||
|
left={
|
||||||
|
<Title>
|
||||||
|
{intl.formatMessage({ id: 'clusters.create.configBasic' })}
|
||||||
|
</Title>
|
||||||
|
}
|
||||||
|
marginTop={0}
|
||||||
|
></PageTools>
|
||||||
|
<Basic
|
||||||
|
action={action}
|
||||||
|
ref={formRef}
|
||||||
|
onFinish={handleOnFinish}
|
||||||
|
currentData={currentData}
|
||||||
|
></Basic>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default BasicForm;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import BasicForm from './basic-form';
|
||||||
|
import ParamertersForm from './parameters-form';
|
||||||
|
import VersionConfigForm from './version-config-form';
|
||||||
|
|
||||||
|
export const moduleMap = {
|
||||||
|
BasicForm: 'BasicForm',
|
||||||
|
VersionConfigForm: 'VersionConfigForm',
|
||||||
|
ParamertersForm: 'ParamertersForm'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const moduleRegistry: Record<string, React.ComponentType<any>> = {
|
||||||
|
[moduleMap.BasicForm]: BasicForm,
|
||||||
|
[moduleMap.VersionConfigForm]: VersionConfigForm,
|
||||||
|
[moduleMap.ParamertersForm]: ParamertersForm
|
||||||
|
};
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import PageTools from '@/components/page-tools';
|
||||||
|
import { PageActionType } from '@/config/types';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { forwardRef, useImperativeHandle, useRef } from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import Parameters from '../forms/parameters';
|
||||||
|
|
||||||
|
const Title = styled.span`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 16px;
|
||||||
|
.text {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
interface BasicFormProps {
|
||||||
|
action: PageActionType;
|
||||||
|
currentData?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ParametersForm = forwardRef((props: BasicFormProps, ref) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const { action, currentData } = props;
|
||||||
|
const formRef = useRef<any>(null);
|
||||||
|
|
||||||
|
const handleOnFinish = (values: any) => {
|
||||||
|
console.log(values);
|
||||||
|
};
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
validateFields: formRef.current?.validateFields,
|
||||||
|
getFieldsValue: formRef.current?.getFieldsValue,
|
||||||
|
submit: formRef.current?.submit
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PageTools
|
||||||
|
marginBottom={16}
|
||||||
|
left={<Title>Parameters Configuration</Title>}
|
||||||
|
marginTop={0}
|
||||||
|
></PageTools>
|
||||||
|
<Parameters
|
||||||
|
action={action}
|
||||||
|
ref={formRef}
|
||||||
|
onFinish={handleOnFinish}
|
||||||
|
currentData={currentData}
|
||||||
|
></Parameters>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ParametersForm;
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { useIntl, useNavigate } from '@umijs/max';
|
||||||
|
import { useMemoizedFn } from 'ahooks';
|
||||||
|
import { useMemo } from 'react';
|
||||||
|
import { moduleMap } from './module-registry';
|
||||||
|
|
||||||
|
export default function useStepList() {
|
||||||
|
const intl = useIntl();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handleBack = useMemoizedFn(() => {
|
||||||
|
navigate(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
return useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
title: 'Basic Info',
|
||||||
|
content: '',
|
||||||
|
showButtons: () => {
|
||||||
|
return {
|
||||||
|
previous: false,
|
||||||
|
next: true,
|
||||||
|
save: false,
|
||||||
|
skip: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
defaultShow: true,
|
||||||
|
showForms: [moduleMap.BasicForm],
|
||||||
|
showModules: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Version Configuration',
|
||||||
|
content: '',
|
||||||
|
showButtons: () => {
|
||||||
|
return {
|
||||||
|
previous: true,
|
||||||
|
next: true,
|
||||||
|
save: false,
|
||||||
|
skip: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
defaultShow: true,
|
||||||
|
showForms: [moduleMap.VersionConfigForm],
|
||||||
|
showModules: []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Parameters',
|
||||||
|
content: '',
|
||||||
|
showButtons: () => {
|
||||||
|
return {
|
||||||
|
previous: true,
|
||||||
|
next: false,
|
||||||
|
save: true,
|
||||||
|
skip: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
defaultShow: true,
|
||||||
|
showForms: [moduleMap.ParamertersForm],
|
||||||
|
showModules: [],
|
||||||
|
beforeNext: handleBack
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[handleBack, intl]
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
import PageTools from '@/components/page-tools';
|
||||||
|
import { PageActionType } from '@/config/types';
|
||||||
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
|
import { Button, FormInstance } from 'antd';
|
||||||
|
import {
|
||||||
|
forwardRef,
|
||||||
|
useEffect,
|
||||||
|
useImperativeHandle,
|
||||||
|
useRef,
|
||||||
|
useState
|
||||||
|
} from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import { FormData } from '../config/types';
|
||||||
|
import VersionsConfig from '../forms/versions-config';
|
||||||
|
|
||||||
|
const PoolContainer = styled.div`
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 16px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const PoolFormWrapper = styled.div`
|
||||||
|
flex: 1;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const Title = styled.span`
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 16px;
|
||||||
|
.text {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
interface WorkerPoolsFormProps {
|
||||||
|
action: PageActionType;
|
||||||
|
currentData?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
const WorkerPoolsForm = forwardRef((props: WorkerPoolsFormProps, ref) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const { action, currentData } = props;
|
||||||
|
const countRef = useRef(0);
|
||||||
|
const formRefs = useRef<Record<number, FormInstance<any> | null>>({});
|
||||||
|
const [activeKey, setActiveKey] = useState<Set<number>>(new Set([0]));
|
||||||
|
const [versionConfigs, setVersionConfigs] = useState<Map<number, FormData>>(
|
||||||
|
new Map([
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
version_name: '',
|
||||||
|
image_name: '',
|
||||||
|
run_command: ''
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]) as Map<number, FormData>
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleOnFinish = (values: any) => {};
|
||||||
|
|
||||||
|
const updateCount = () => {
|
||||||
|
countRef.current += 1;
|
||||||
|
return countRef.current;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddPool = () => {
|
||||||
|
const newId = updateCount();
|
||||||
|
setVersionConfigs((prev) =>
|
||||||
|
new Map(prev).set(newId, {
|
||||||
|
name: ``
|
||||||
|
} as FormData)
|
||||||
|
);
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
setActiveKey((prev) => new Set([newId]));
|
||||||
|
window.scrollTo({
|
||||||
|
top: document.body.scrollHeight,
|
||||||
|
behavior: 'smooth'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemovePool = (id: number) => {
|
||||||
|
if (versionConfigs.size === 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setVersionConfigs((prev) => {
|
||||||
|
const newList = new Map(prev);
|
||||||
|
newList.delete(id);
|
||||||
|
return newList;
|
||||||
|
});
|
||||||
|
formRefs.current[id] = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
const gatherFormValues = (results: PromiseSettledResult<any>[]) => {
|
||||||
|
const resultList = results.map((result: PromiseSettledResult<any>) => {
|
||||||
|
if (result.status === 'fulfilled') {
|
||||||
|
return result.value;
|
||||||
|
}
|
||||||
|
if (result.status === 'rejected') {
|
||||||
|
return result.reason?.values || {};
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
console.log('gatherFormValues========', resultList);
|
||||||
|
return resultList.filter((item) => item);
|
||||||
|
};
|
||||||
|
|
||||||
|
const validateFields = async () => {
|
||||||
|
const promises = Array.from(Object.values(formRefs.current)).map((form) =>
|
||||||
|
form?.validateFields()
|
||||||
|
);
|
||||||
|
const results = await Promise.allSettled(promises);
|
||||||
|
console.log('results========0', promises, results);
|
||||||
|
if (results.some((result) => result.status === 'rejected')) {
|
||||||
|
return Promise.reject({
|
||||||
|
values: {
|
||||||
|
worker_pools: gatherFormValues(results)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.resolve({
|
||||||
|
worker_pools: gatherFormValues(results)
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const setFieldsValue = (data: any) => {
|
||||||
|
const worker_pools = data.worker_pools || [];
|
||||||
|
const newWorkerPools = worker_pools.map(
|
||||||
|
(poolData: FormData, index: number) => [index, poolData]
|
||||||
|
);
|
||||||
|
console.log('newWorkerPools========', newWorkerPools);
|
||||||
|
setVersionConfigs(new Map(newWorkerPools));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getFieldsValue = () => {
|
||||||
|
const values = Object.values(formRefs.current).map((form) =>
|
||||||
|
form?.getFieldsValue()
|
||||||
|
);
|
||||||
|
console.log('getFieldsValue========', values);
|
||||||
|
return {
|
||||||
|
worker_pools: values
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOnToggle = (open: boolean, key: number) => {
|
||||||
|
console.log('Active keys changed:', key);
|
||||||
|
if (open) {
|
||||||
|
setActiveKey((prev) => new Set([key]));
|
||||||
|
} else {
|
||||||
|
setActiveKey((prev) => {
|
||||||
|
const newSet = new Set(prev);
|
||||||
|
newSet.delete(key);
|
||||||
|
return newSet;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
useImperativeHandle(ref, () => ({
|
||||||
|
submit: () => {},
|
||||||
|
validateFields: validateFields,
|
||||||
|
getFieldsValue: getFieldsValue
|
||||||
|
}));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentData) {
|
||||||
|
console.log('currentData===========1=', currentData);
|
||||||
|
setFieldsValue(currentData);
|
||||||
|
}
|
||||||
|
}, [currentData]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<PageTools
|
||||||
|
marginBottom={22}
|
||||||
|
marginTop={0}
|
||||||
|
left={
|
||||||
|
<span className="flex-center gap-16">
|
||||||
|
<Title>Backend Versions</Title>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
right={
|
||||||
|
<Button
|
||||||
|
onClick={handleAddPool}
|
||||||
|
variant="filled"
|
||||||
|
color="default"
|
||||||
|
icon={<PlusOutlined />}
|
||||||
|
>
|
||||||
|
Add Version
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
></PageTools>
|
||||||
|
|
||||||
|
{Array.from(versionConfigs.keys()).map((key, index) => (
|
||||||
|
<PoolContainer key={key}>
|
||||||
|
<PoolFormWrapper>
|
||||||
|
<VersionsConfig
|
||||||
|
name={`versionForm_${key}`}
|
||||||
|
action={action}
|
||||||
|
ref={(el: any) => {
|
||||||
|
if (el) {
|
||||||
|
formRefs.current[key] = el;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
collapseProps={{
|
||||||
|
collapsible: true,
|
||||||
|
open: activeKey.has(key),
|
||||||
|
defaultOpen: activeKey.has(key),
|
||||||
|
onToggle: (open: boolean) => handleOnToggle(open, key)
|
||||||
|
}}
|
||||||
|
showDelete={versionConfigs.size > 1}
|
||||||
|
onFinish={handleOnFinish}
|
||||||
|
currentData={versionConfigs.get(key)}
|
||||||
|
onDelete={() => handleRemovePool(key)}
|
||||||
|
></VersionsConfig>
|
||||||
|
</PoolFormWrapper>
|
||||||
|
</PoolContainer>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default WorkerPoolsForm;
|
||||||
@@ -5,7 +5,7 @@ import BaseSelect from '@/components/seal-form/base/select';
|
|||||||
import { PageAction } from '@/config';
|
import { PageAction } from '@/config';
|
||||||
import useBodyScroll from '@/hooks/use-body-scroll';
|
import useBodyScroll from '@/hooks/use-body-scroll';
|
||||||
import { IS_FIRST_LOGIN, writeState } from '@/utils/localstore/index';
|
import { IS_FIRST_LOGIN, writeState } from '@/utils/localstore/index';
|
||||||
import { SyncOutlined } from '@ant-design/icons';
|
import { SearchOutlined, SyncOutlined } from '@ant-design/icons';
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
import { useIntl, useNavigate } from '@umijs/max';
|
import { useIntl, useNavigate } from '@umijs/max';
|
||||||
import { Button, Input, Pagination, Space, message } from 'antd';
|
import { Button, Input, Pagination, Space, message } from 'antd';
|
||||||
@@ -266,6 +266,11 @@ const Catalog: React.FC = () => {
|
|||||||
left={
|
left={
|
||||||
<Space>
|
<Space>
|
||||||
<Input
|
<Input
|
||||||
|
prefix={
|
||||||
|
<SearchOutlined
|
||||||
|
style={{ color: 'var(--ant-color-text-placeholder)' }}
|
||||||
|
></SearchOutlined>
|
||||||
|
}
|
||||||
placeholder={intl.formatMessage({ id: 'common.filter.name' })}
|
placeholder={intl.formatMessage({ id: 'common.filter.name' })}
|
||||||
style={{ width: 230 }}
|
style={{ width: 230 }}
|
||||||
size="large"
|
size="large"
|
||||||
|
|||||||
@@ -161,7 +161,6 @@ const DataForm: React.FC<DataFormProps> = forwardRef((props, ref) => {
|
|||||||
form={form}
|
form={form}
|
||||||
onFinish={handleOk}
|
onFinish={handleOk}
|
||||||
preserve={false}
|
preserve={false}
|
||||||
style={{ padding: '16px 24px' }}
|
|
||||||
clearOnDestroy={true}
|
clearOnDestroy={true}
|
||||||
onValuesChange={handleOnValuesChange}
|
onValuesChange={handleOnValuesChange}
|
||||||
scrollToFirstError={true}
|
scrollToFirstError={true}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { Button } from 'antd';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import ColumnWrapper from '../../_components/column-wrapper';
|
||||||
import { queryCatalogItemSpec } from '../apis';
|
import { queryCatalogItemSpec } from '../apis';
|
||||||
import {
|
import {
|
||||||
defaultFormValues,
|
defaultFormValues,
|
||||||
@@ -21,7 +22,6 @@ import { CatalogSpec, FormData, ListItem, SourceType } from '../config/types';
|
|||||||
import { generateGPUIds } from '../config/utils';
|
import { generateGPUIds } from '../config/utils';
|
||||||
import { useCheckCompatibility } from '../hooks';
|
import { useCheckCompatibility } from '../hooks';
|
||||||
import useFormInitialValues from '../hooks/use-form-initial-values';
|
import useFormInitialValues from '../hooks/use-form-initial-values';
|
||||||
import ColumnWrapper from './column-wrapper';
|
|
||||||
import CompatibilityAlert from './compatible-alert';
|
import CompatibilityAlert from './compatible-alert';
|
||||||
import DataForm from './data-form';
|
import DataForm from './data-form';
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { Button } from 'antd';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
|
import ColumnWrapper from '../../_components/column-wrapper';
|
||||||
import { defaultFormValues, deployFormKeyMap, modelSourceMap } from '../config';
|
import { defaultFormValues, deployFormKeyMap, modelSourceMap } from '../config';
|
||||||
import { backendOptionsMap } from '../config/backend-parameters';
|
import { backendOptionsMap } from '../config/backend-parameters';
|
||||||
import { BackendOption, FormData, SourceType } from '../config/types';
|
import { BackendOption, FormData, SourceType } from '../config/types';
|
||||||
@@ -17,7 +18,6 @@ import {
|
|||||||
useSelectModel
|
useSelectModel
|
||||||
} from '../hooks';
|
} from '../hooks';
|
||||||
import useCheckBackend from '../hooks/use-check-backend';
|
import useCheckBackend from '../hooks/use-check-backend';
|
||||||
import ColumnWrapper from './column-wrapper';
|
|
||||||
import CompatibilityAlert from './compatible-alert';
|
import CompatibilityAlert from './compatible-alert';
|
||||||
import DataForm from './data-form';
|
import DataForm from './data-form';
|
||||||
import GGUFResult from './gguf-result';
|
import GGUFResult from './gguf-result';
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import useTableRowSelection from '@/hooks/use-table-row-selection';
|
|||||||
import useTableSort from '@/hooks/use-table-sort';
|
import useTableSort from '@/hooks/use-table-sort';
|
||||||
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
import { ListItem as WorkerListItem } from '@/pages/resources/config/types';
|
||||||
import { handleBatchRequest } from '@/utils';
|
import { handleBatchRequest } from '@/utils';
|
||||||
import { DownOutlined, SyncOutlined } from '@ant-design/icons';
|
import { DownOutlined, SearchOutlined, SyncOutlined } from '@ant-design/icons';
|
||||||
import { PageContainer } from '@ant-design/pro-components';
|
import { PageContainer } from '@ant-design/pro-components';
|
||||||
import { useIntl, useNavigate } from '@umijs/max';
|
import { useIntl, useNavigate } from '@umijs/max';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
@@ -579,6 +579,11 @@ const Models: React.FC<ModelsProps> = ({
|
|||||||
left={
|
left={
|
||||||
<Space>
|
<Space>
|
||||||
<Input
|
<Input
|
||||||
|
prefix={
|
||||||
|
<SearchOutlined
|
||||||
|
style={{ color: 'var(--ant-color-text-placeholder)' }}
|
||||||
|
></SearchOutlined>
|
||||||
|
}
|
||||||
placeholder={intl.formatMessage({ id: 'common.filter.name' })}
|
placeholder={intl.formatMessage({ id: 'common.filter.name' })}
|
||||||
style={{ width: 160 }}
|
style={{ width: 160 }}
|
||||||
size="large"
|
size="large"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useIntl } from '@umijs/max';
|
|||||||
import { Button, Modal } from 'antd';
|
import { Button, Modal } from 'antd';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import React, { useEffect, useMemo, useRef } from 'react';
|
import React, { useEffect, useMemo, useRef } from 'react';
|
||||||
|
import ColumnWrapper from '../../_components/column-wrapper';
|
||||||
import {
|
import {
|
||||||
deployFormKeyMap,
|
deployFormKeyMap,
|
||||||
modelSourceMap,
|
modelSourceMap,
|
||||||
@@ -14,7 +15,6 @@ import { backendOptionsMap } from '../config/backend-parameters';
|
|||||||
import { BackendOption, FormData } from '../config/types';
|
import { BackendOption, FormData } from '../config/types';
|
||||||
import { generateGPUSelector } from '../config/utils';
|
import { generateGPUSelector } from '../config/utils';
|
||||||
import { useCheckCompatibility } from '../hooks';
|
import { useCheckCompatibility } from '../hooks';
|
||||||
import ColumnWrapper from './column-wrapper';
|
|
||||||
import CompatibilityAlert from './compatible-alert';
|
import CompatibilityAlert from './compatible-alert';
|
||||||
import DataForm from './data-form';
|
import DataForm from './data-form';
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { ProviderValueMap } from '@/pages/cluster-management/config';
|
|||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import ColumnWrapper from '../components/column-wrapper';
|
import ColumnWrapper from '../../_components/column-wrapper';
|
||||||
import CompatibilityAlert from '../components/compatible-alert';
|
import CompatibilityAlert from '../components/compatible-alert';
|
||||||
import ModelCard from '../components/model-card';
|
import ModelCard from '../components/model-card';
|
||||||
import SearchModel from '../components/search-model';
|
import SearchModel from '../components/search-model';
|
||||||
|
|||||||
@@ -87,7 +87,6 @@ const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
|
|||||||
form={form}
|
form={form}
|
||||||
onFinish={handleOk}
|
onFinish={handleOk}
|
||||||
preserve={false}
|
preserve={false}
|
||||||
style={{ padding: '16px 24px' }}
|
|
||||||
clearOnDestroy={true}
|
clearOnDestroy={true}
|
||||||
initialValues={{
|
initialValues={{
|
||||||
source: source
|
source: source
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import EditorWrap from '@/components/editor-wrap';
|
|
||||||
import HighlightCode from '@/components/highlight-code';
|
|
||||||
import { BulbOutlined } from '@ant-design/icons';
|
import { BulbOutlined } from '@ant-design/icons';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button, Modal } from 'antd';
|
import { Button, Modal } from 'antd';
|
||||||
import React, { useMemo, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import CommandViewer from '../../_components/command-viewer';
|
||||||
|
|
||||||
type ViewModalProps = {
|
type ViewModalProps = {
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -32,30 +31,30 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
|
|||||||
const { title, open, onCancel, viewCodeContent } = props || {};
|
const { title, open, onCancel, viewCodeContent } = props || {};
|
||||||
|
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const [lang, setLang] = useState<string>(langMap.shell);
|
const [codeValue, setCodeValue] = useState<string>(viewCodeContent?.curlCode);
|
||||||
|
|
||||||
const codeValue = useMemo(() => {
|
|
||||||
if (lang === langMap.shell) {
|
|
||||||
return viewCodeContent?.curlCode;
|
|
||||||
}
|
|
||||||
if (lang === langMap.javascript) {
|
|
||||||
return viewCodeContent?.nodeJsCode;
|
|
||||||
}
|
|
||||||
if (lang === langMap.python) {
|
|
||||||
return viewCodeContent?.pythonCode;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}, [lang, viewCodeContent]);
|
|
||||||
|
|
||||||
const handleOnChangeLang = (value: string | number) => {
|
const handleOnChangeLang = (value: string | number) => {
|
||||||
setLang(value as string);
|
if (value === langMap.shell) {
|
||||||
|
setCodeValue(viewCodeContent?.curlCode);
|
||||||
|
}
|
||||||
|
if (value === langMap.javascript) {
|
||||||
|
setCodeValue(viewCodeContent?.nodeJsCode);
|
||||||
|
}
|
||||||
|
if (value === langMap.python) {
|
||||||
|
setCodeValue(viewCodeContent?.pythonCode);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
setLang(langMap.shell);
|
|
||||||
onCancel();
|
onCancel();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
setCodeValue(viewCodeContent?.curlCode);
|
||||||
|
}
|
||||||
|
}, [open]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Modal
|
<Modal
|
||||||
@@ -74,26 +73,13 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
|
|||||||
{intl.formatMessage({ id: 'playground.viewcode.info' })}
|
{intl.formatMessage({ id: 'playground.viewcode.info' })}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<EditorWrap
|
<CommandViewer
|
||||||
|
code={codeValue}
|
||||||
copyText={codeValue}
|
copyText={codeValue}
|
||||||
langOptions={langOptions}
|
options={langOptions}
|
||||||
defaultValue={langMap.shell}
|
defaultValue={langMap.shell}
|
||||||
showHeader={true}
|
onChange={handleOnChangeLang}
|
||||||
onChangeLang={handleOnChangeLang}
|
></CommandViewer>
|
||||||
styles={{
|
|
||||||
wrapper: {
|
|
||||||
backgroundColor: 'var(--color-editor-dark)'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<HighlightCode
|
|
||||||
height={380}
|
|
||||||
theme="dark"
|
|
||||||
code={codeValue}
|
|
||||||
lang={lang}
|
|
||||||
copyable={false}
|
|
||||||
></HighlightCode>
|
|
||||||
</EditorWrap>
|
|
||||||
<div
|
<div
|
||||||
style={{ marginTop: 10, display: 'flex', alignItems: 'baseline' }}
|
style={{ marginTop: 10, display: 'flex', alignItems: 'baseline' }}
|
||||||
>
|
>
|
||||||
@@ -128,4 +114,4 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default React.memo(ViewCodeModal);
|
export default ViewCodeModal;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { GPUStackVersionAtom } from '@/atoms/user';
|
import { GPUStackVersionAtom } from '@/atoms/user';
|
||||||
import { getAtomStorage } from '@/atoms/utils';
|
import { getAtomStorage } from '@/atoms/utils';
|
||||||
import EditorWrap from '@/components/editor-wrap';
|
|
||||||
import HighlightCode from '@/components/highlight-code';
|
import HighlightCode from '@/components/highlight-code';
|
||||||
|
import CommandViewer from '@/pages/_components/command-viewer';
|
||||||
import { useIntl } from '@umijs/max';
|
import { useIntl } from '@umijs/max';
|
||||||
import { Radio } from 'antd';
|
import { Radio } from 'antd';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
@@ -98,27 +98,15 @@ const AddWorker: React.FC<ViewModalProps> = (props) => {
|
|||||||
></div>
|
></div>
|
||||||
)}
|
)}
|
||||||
{activeKey === 'npu' ? (
|
{activeKey === 'npu' ? (
|
||||||
<EditorWrap
|
<CommandViewer
|
||||||
headerHeight={32}
|
headerHeight={32}
|
||||||
copyText={code}
|
copyText={code}
|
||||||
langOptions={npuOptions}
|
options={npuOptions}
|
||||||
defaultValue="npu"
|
defaultValue="npu"
|
||||||
showHeader={true}
|
onChange={handleOnChange}
|
||||||
onChangeLang={handleOnChange}
|
lang="bash"
|
||||||
styles={{
|
code={code.replace(/\\/g, '')}
|
||||||
wrapper: {
|
></CommandViewer>
|
||||||
backgroundColor: 'var(--color-editor-dark)'
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<HighlightCode
|
|
||||||
theme="dark"
|
|
||||||
code={code.replace(/\\/g, '')}
|
|
||||||
copyValue={code}
|
|
||||||
lang="bash"
|
|
||||||
copyable={false}
|
|
||||||
></HighlightCode>
|
|
||||||
</EditorWrap>
|
|
||||||
) : (
|
) : (
|
||||||
<HighlightCode
|
<HighlightCode
|
||||||
theme="dark"
|
theme="dark"
|
||||||
|
|||||||
Reference in New Issue
Block a user