fix(style): k8s form
This commit is contained in:
+1
-1
@@ -17,7 +17,7 @@
|
||||
"@ant-design/pro-components": "3.1.0-0",
|
||||
"@antv/g6": "^5.0.51",
|
||||
"@braintree/sanitize-url": "^7.1.1",
|
||||
"@gpustack/core-ui": "^1.0.20",
|
||||
"@gpustack/core-ui": "^1.0.21",
|
||||
"@huggingface/gguf": "^0.1.7",
|
||||
"@huggingface/hub": "^0.15.1",
|
||||
"@huggingface/tasks": "^0.11.6",
|
||||
|
||||
@@ -30,7 +30,7 @@ export default {
|
||||
'menu.usage': '使用量',
|
||||
'menu.404': '404',
|
||||
'menu.resources.workers': '节点',
|
||||
'menu.resources.gpus': 'GPUs',
|
||||
'menu.resources.gpus': 'GPU',
|
||||
'menu.models.modelfiles': '模型文件',
|
||||
'menu.accessControl': '访问控制',
|
||||
'menu.accessControl.apikeys': 'API 密钥',
|
||||
|
||||
@@ -276,7 +276,9 @@ const VersionsForm: React.FC<AddModalProps> = ({
|
||||
key={name}
|
||||
defaultOpen
|
||||
styles={{
|
||||
body: collapseKey.has(name) ? { padding: 16 } : {},
|
||||
body: collapseKey.has(name)
|
||||
? { paddingInline: 16, paddingBlock: '16px 0' }
|
||||
: {},
|
||||
content: { paddingTop: 0 },
|
||||
header: {
|
||||
backgroundColor: 'unset'
|
||||
|
||||
@@ -11,8 +11,14 @@ import {
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import React, { forwardRef, useEffect, useImperativeHandle } from 'react';
|
||||
import React, {
|
||||
forwardRef,
|
||||
useEffect,
|
||||
useImperativeHandle,
|
||||
useState
|
||||
} from 'react';
|
||||
import { ProviderType, ProviderValueMap } from '../config';
|
||||
import { FormContext } from '../config/form-context';
|
||||
import {
|
||||
ClusterFormData as FormData,
|
||||
ClusterListItem as ListItem
|
||||
@@ -37,6 +43,7 @@ const ClusterForm: React.FC<AddModalProps> = forwardRef(
|
||||
const [k8sActiveKey, setK8sActiveKey] = React.useState<string[]>([
|
||||
'k8sOptions'
|
||||
]);
|
||||
const [submitAttempted, setSubmitAttempted] = useState(false);
|
||||
const advanceConfigRef = React.useRef<any>(null);
|
||||
const systemConfig = useAtomValue(systemConfigAtom);
|
||||
|
||||
@@ -171,7 +178,12 @@ const ClusterForm: React.FC<AddModalProps> = forwardRef(
|
||||
};
|
||||
},
|
||||
validateFields: async () => {
|
||||
await form.validateFields();
|
||||
try {
|
||||
await form.validateFields();
|
||||
} catch (e) {
|
||||
setSubmitAttempted(true);
|
||||
throw e;
|
||||
}
|
||||
const values = form.getFieldsValue(true);
|
||||
|
||||
const workerConfig = yaml2Json(
|
||||
@@ -187,103 +199,112 @@ const ClusterForm: React.FC<AddModalProps> = forwardRef(
|
||||
}
|
||||
}));
|
||||
|
||||
const handleOnFinishFailed = () => {
|
||||
setSubmitAttempted(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
name="clusterForm"
|
||||
form={form}
|
||||
onFinish={handleOnFinish}
|
||||
preserve={false}
|
||||
scrollToFirstError={true}
|
||||
initialValues={currentData}
|
||||
>
|
||||
<Form.Item<FormData>
|
||||
name="name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage(
|
||||
{ id: 'common.form.rule.input' },
|
||||
<FormContext.Provider value={{ submitAttempted }}>
|
||||
<Form
|
||||
name="clusterForm"
|
||||
form={form}
|
||||
onFinish={handleOnFinish}
|
||||
onFinishFailed={handleOnFinishFailed}
|
||||
preserve={false}
|
||||
scrollToFirstError={true}
|
||||
initialValues={currentData}
|
||||
>
|
||||
<Form.Item<FormData>
|
||||
name="name"
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: intl.formatMessage(
|
||||
{ id: 'common.form.rule.input' },
|
||||
{
|
||||
name: intl.formatMessage({ id: 'common.table.name' })
|
||||
}
|
||||
)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<CInput.Input
|
||||
label={intl.formatMessage({ id: 'common.table.name' })}
|
||||
required
|
||||
trim={false}
|
||||
></CInput.Input>
|
||||
</Form.Item>
|
||||
<PluginExtraFields name="CreateOrgScopeField" context={{ action }} />
|
||||
{provider === ProviderValueMap.DigitalOcean && (
|
||||
<CloudProvider
|
||||
provider={provider}
|
||||
action={action}
|
||||
credentialID={currentData?.credential_id}
|
||||
credentialList={credentialList}
|
||||
></CloudProvider>
|
||||
)}
|
||||
|
||||
<Form.Item<FormData>
|
||||
name="description"
|
||||
rules={[{ required: false }]}
|
||||
style={{ marginBottom: 8 }}
|
||||
>
|
||||
<SealTextArea
|
||||
scaleSize
|
||||
label={intl.formatMessage({ id: 'common.table.description' })}
|
||||
></SealTextArea>
|
||||
</Form.Item>
|
||||
|
||||
{provider === ProviderValueMap.Kubernetes && (
|
||||
<CollapsePanel
|
||||
accordion={false}
|
||||
activeKey={k8sActiveKey}
|
||||
onChange={(keys) =>
|
||||
setK8sActiveKey(Array.isArray(keys) ? keys : [keys])
|
||||
}
|
||||
items={[
|
||||
{
|
||||
name: intl.formatMessage({ id: 'common.table.name' })
|
||||
key: 'k8sOptions',
|
||||
label: intl.formatMessage({
|
||||
id: 'clusters.k8sOptions.title'
|
||||
}),
|
||||
forceRender: true,
|
||||
children: (
|
||||
<K8sPodSpec
|
||||
key={currentData?.id ?? 'new'}
|
||||
action={action}
|
||||
initialGpuInstanceOptions={
|
||||
currentData?.k8s_options?.gpuInstanceOptions
|
||||
}
|
||||
></K8sPodSpec>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
]}
|
||||
>
|
||||
<CInput.Input
|
||||
label={intl.formatMessage({ id: 'common.table.name' })}
|
||||
required
|
||||
trim={false}
|
||||
></CInput.Input>
|
||||
</Form.Item>
|
||||
<PluginExtraFields name="CreateOrgScopeField" context={{ action }} />
|
||||
{provider === ProviderValueMap.DigitalOcean && (
|
||||
<CloudProvider
|
||||
provider={provider}
|
||||
action={action}
|
||||
credentialID={currentData?.credential_id}
|
||||
credentialList={credentialList}
|
||||
></CloudProvider>
|
||||
)}
|
||||
]}
|
||||
></CollapsePanel>
|
||||
)}
|
||||
|
||||
<Form.Item<FormData>
|
||||
name="description"
|
||||
rules={[{ required: false }]}
|
||||
style={{ marginBottom: 8 }}
|
||||
>
|
||||
<SealTextArea
|
||||
scaleSize
|
||||
label={intl.formatMessage({ id: 'common.table.description' })}
|
||||
></SealTextArea>
|
||||
</Form.Item>
|
||||
|
||||
{provider === ProviderValueMap.Kubernetes && (
|
||||
<CollapsePanel
|
||||
accordion={false}
|
||||
activeKey={k8sActiveKey}
|
||||
onChange={(keys) =>
|
||||
setK8sActiveKey(Array.isArray(keys) ? keys : [keys])
|
||||
}
|
||||
activeKey={activeKey}
|
||||
onChange={handleOnCollapseChange}
|
||||
items={[
|
||||
{
|
||||
key: 'k8sOptions',
|
||||
label: intl.formatMessage({ id: 'clusters.k8sOptions.title' }),
|
||||
key: 'advanceConfig',
|
||||
label: intl.formatMessage({ id: 'resources.form.advanced' }),
|
||||
forceRender: true,
|
||||
children: (
|
||||
<K8sPodSpec
|
||||
key={currentData?.id ?? 'new'}
|
||||
<AdvanceConfig
|
||||
action={action}
|
||||
initialGpuInstanceOptions={
|
||||
currentData?.k8s_options?.gpuInstanceOptions
|
||||
}
|
||||
></K8sPodSpec>
|
||||
provider={provider}
|
||||
currentData={currentData}
|
||||
ref={advanceConfigRef}
|
||||
></AdvanceConfig>
|
||||
)
|
||||
}
|
||||
]}
|
||||
></CollapsePanel>
|
||||
)}
|
||||
|
||||
<CollapsePanel
|
||||
accordion={false}
|
||||
activeKey={activeKey}
|
||||
onChange={handleOnCollapseChange}
|
||||
items={[
|
||||
{
|
||||
key: 'advanceConfig',
|
||||
label: intl.formatMessage({ id: 'resources.form.advanced' }),
|
||||
forceRender: true,
|
||||
children: (
|
||||
<AdvanceConfig
|
||||
action={action}
|
||||
provider={provider}
|
||||
currentData={currentData}
|
||||
ref={advanceConfigRef}
|
||||
></AdvanceConfig>
|
||||
)
|
||||
}
|
||||
]}
|
||||
></CollapsePanel>
|
||||
</Form>
|
||||
</Form>
|
||||
</FormContext.Provider>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { Input as CInput } from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Divider } from 'antd';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { ImageCredential } from '../config/types';
|
||||
|
||||
const Wrapper = styled.div`
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
interface CredentialItemProps {
|
||||
item: ImageCredential;
|
||||
validated: boolean;
|
||||
index: number;
|
||||
onChange: (partial: Partial<ImageCredential>) => void;
|
||||
}
|
||||
|
||||
const CredentialItem: React.FC<CredentialItemProps> = ({
|
||||
item,
|
||||
index,
|
||||
validated,
|
||||
onChange
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const registryEmpty = !item.registry?.trim();
|
||||
const registryStatus =
|
||||
validated && registryEmpty ? ('error' as const) : ('success' as const);
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
{index !== 0 && (
|
||||
<Divider style={{ marginBlock: 8 }} variant="dashed"></Divider>
|
||||
)}
|
||||
<CInput.Input
|
||||
required
|
||||
status={registryStatus}
|
||||
value={item.registry}
|
||||
onChange={(e) => onChange({ registry: e.target.value })}
|
||||
label={intl.formatMessage({
|
||||
id: 'clusters.imageCredentials.registry'
|
||||
})}
|
||||
/>
|
||||
<div className="row">
|
||||
<CInput.Input
|
||||
value={item.username}
|
||||
status="success"
|
||||
onChange={(e) => onChange({ username: e.target.value })}
|
||||
label={intl.formatMessage({
|
||||
id: 'clusters.imageCredentials.username'
|
||||
})}
|
||||
/>
|
||||
<CInput.Password
|
||||
status="success"
|
||||
value={item.password}
|
||||
onChange={(e) => onChange({ password: e.target.value })}
|
||||
label={intl.formatMessage({
|
||||
id: 'clusters.imageCredentials.password'
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default CredentialItem;
|
||||
@@ -0,0 +1,98 @@
|
||||
import { MetadataList, useAppUtils } from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Form } from 'antd';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useFormContext } from '../config/form-context';
|
||||
import { ImageCredential as ImageCredentialType } from '../config/types';
|
||||
import imgCredentialStyle from '../styles/img-credential.less';
|
||||
import CredentialItem from './credential-item';
|
||||
|
||||
const FIELD_PATH = ['k8s_options', 'imageCredentials'];
|
||||
|
||||
const ImageCredential: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const { getRuleMessage } = useAppUtils();
|
||||
const { submitAttempted } = useFormContext();
|
||||
const validated = !!submitAttempted;
|
||||
const form = Form.useFormInstance();
|
||||
const credentials: ImageCredentialType[] =
|
||||
Form.useWatch(FIELD_PATH, form) || [];
|
||||
|
||||
const updateList = (list: ImageCredentialType[]) => {
|
||||
form.setFieldValue(FIELD_PATH, list);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (validated) {
|
||||
form.validateFields([FIELD_PATH]).catch(() => {});
|
||||
}
|
||||
}, [credentials, validated]);
|
||||
|
||||
const handleAdd = () => {
|
||||
updateList([...credentials, { registry: '', username: '', password: '' }]);
|
||||
};
|
||||
|
||||
const handleDelete = (index: number) => {
|
||||
const next = [...credentials];
|
||||
next.splice(index, 1);
|
||||
updateList(next);
|
||||
};
|
||||
|
||||
const handleChange = (
|
||||
index: number,
|
||||
partial: Partial<ImageCredentialType>
|
||||
) => {
|
||||
const next = [...credentials];
|
||||
next[index] = { ...next[index], ...partial };
|
||||
updateList(next);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={imgCredentialStyle.container}>
|
||||
<Form.Item
|
||||
name={FIELD_PATH}
|
||||
style={{ marginTop: 24 }}
|
||||
rules={[
|
||||
{
|
||||
validator: async (_, value: ImageCredentialType[]) => {
|
||||
if (!value?.length) return;
|
||||
const hasMissingRegistry = value.some(
|
||||
(item) => !item?.registry?.trim()
|
||||
);
|
||||
if (hasMissingRegistry) {
|
||||
throw new Error(
|
||||
getRuleMessage('input', 'clusters.imageCredentials.registry')
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
]}
|
||||
>
|
||||
<MetadataList
|
||||
label={intl.formatMessage({ id: 'clusters.imageCredentials.title' })}
|
||||
btnText={intl.formatMessage({ id: 'clusters.imageCredentials.add' })}
|
||||
dataList={credentials}
|
||||
onAdd={handleAdd}
|
||||
onDelete={handleDelete}
|
||||
styles={{
|
||||
delBtn: {
|
||||
marginTop: 44
|
||||
}
|
||||
}}
|
||||
>
|
||||
{(item: ImageCredentialType, index: number) => (
|
||||
<CredentialItem
|
||||
item={item}
|
||||
index={index}
|
||||
key={index}
|
||||
validated={validated}
|
||||
onChange={(partial) => handleChange(index, partial)}
|
||||
/>
|
||||
)}
|
||||
</MetadataList>
|
||||
</Form.Item>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImageCredential;
|
||||
@@ -1,20 +1,11 @@
|
||||
import { PageActionType } from '@/config/types';
|
||||
import {
|
||||
MinusOutlined,
|
||||
PlusOutlined,
|
||||
QuestionCircleOutlined
|
||||
} from '@ant-design/icons';
|
||||
import {
|
||||
Input as CInput,
|
||||
LabelSelector,
|
||||
SwitchCard,
|
||||
useAppUtils
|
||||
} from '@gpustack/core-ui';
|
||||
import { Input as CInput, LabelSelector, SwitchCard } from '@gpustack/core-ui';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Form, Tooltip } from 'antd';
|
||||
import { Form } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { GpuInstanceOptions } from '../config/types';
|
||||
import ImageCredential from './image-credential';
|
||||
import K8SVolumeMount from './k8s-volume-mount';
|
||||
|
||||
const Title = styled.div`
|
||||
@@ -32,151 +23,21 @@ const SectionWrap = styled.div`
|
||||
margin-bottom: 16px;
|
||||
`;
|
||||
|
||||
const ImageCredentialsForm: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
const { getRuleMessage } = useAppUtils();
|
||||
|
||||
return (
|
||||
<SectionWrap>
|
||||
<Form.List name={['k8s_options', 'imageCredentials']}>
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
<Title>
|
||||
<div className="flex-center gap-8">
|
||||
<span>
|
||||
{intl.formatMessage({
|
||||
id: 'clusters.imageCredentials.title'
|
||||
})}
|
||||
</span>
|
||||
<Button
|
||||
type="link"
|
||||
onClick={() =>
|
||||
add({ registry: '', username: '', password: '' })
|
||||
}
|
||||
>
|
||||
<PlusOutlined />{' '}
|
||||
{intl.formatMessage({
|
||||
id: 'clusters.imageCredentials.add'
|
||||
})}
|
||||
</Button>
|
||||
</div>
|
||||
</Title>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
{fields.map(({ key, name }) => (
|
||||
<div
|
||||
key={key}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'flex-start',
|
||||
gap: 8,
|
||||
padding: 12,
|
||||
border: '1px solid var(--ant-color-split)',
|
||||
borderRadius: 'var(--ant-border-radius-lg)'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 12
|
||||
}}
|
||||
>
|
||||
<Form.Item
|
||||
name={[name, 'registry']}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: getRuleMessage(
|
||||
'input',
|
||||
'clusters.imageCredentials.registry'
|
||||
)
|
||||
}
|
||||
]}
|
||||
style={{ marginBottom: 0 }}
|
||||
>
|
||||
<CInput.Input
|
||||
required
|
||||
label={intl.formatMessage({
|
||||
id: 'clusters.imageCredentials.registry'
|
||||
})}
|
||||
></CInput.Input>
|
||||
</Form.Item>
|
||||
<div style={{ display: 'flex', gap: 12 }}>
|
||||
<div style={{ flex: 1 }}>
|
||||
<Form.Item
|
||||
name={[name, 'username']}
|
||||
style={{ marginBottom: 0 }}
|
||||
>
|
||||
<CInput.Input
|
||||
label={intl.formatMessage({
|
||||
id: 'clusters.imageCredentials.username'
|
||||
})}
|
||||
></CInput.Input>
|
||||
</Form.Item>
|
||||
</div>
|
||||
<div style={{ flex: 1 }}>
|
||||
<Form.Item
|
||||
name={[name, 'password']}
|
||||
style={{ marginBottom: 0 }}
|
||||
>
|
||||
<CInput.Password
|
||||
label={intl.formatMessage({
|
||||
id: 'clusters.imageCredentials.password'
|
||||
})}
|
||||
></CInput.Password>
|
||||
</Form.Item>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
size="small"
|
||||
shape="circle"
|
||||
style={{ marginTop: 8 }}
|
||||
onClick={() => remove(name)}
|
||||
>
|
||||
<MinusOutlined />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
</SectionWrap>
|
||||
);
|
||||
};
|
||||
|
||||
const NodeSelectorForm: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
return (
|
||||
<SectionWrap>
|
||||
<Title>
|
||||
<span className="flex-center gap-4">
|
||||
<span>
|
||||
{intl.formatMessage({ id: 'clusters.nodeSelector.title' })}
|
||||
</span>
|
||||
<Tooltip
|
||||
title={intl.formatMessage({ id: 'clusters.nodeSelector.tip' })}
|
||||
>
|
||||
<QuestionCircleOutlined
|
||||
style={{ color: 'var(--ant-color-text-secondary)' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</span>
|
||||
</Title>
|
||||
<Form.Item name={['k8s_options', 'nodeSelector']}>
|
||||
<LabelSelector
|
||||
label={intl.formatMessage({ id: 'clusters.nodeSelector.title' })}
|
||||
description={intl.formatMessage({ id: 'clusters.nodeSelector.tip' })}
|
||||
></LabelSelector>
|
||||
</Form.Item>
|
||||
</SectionWrap>
|
||||
);
|
||||
};
|
||||
|
||||
// Render namespace. Kept as the first field of the section so the most
|
||||
// fundamental K8s deployment knob is set before the rest.
|
||||
const NamespaceForm: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
@@ -197,8 +58,6 @@ const NamespaceForm: React.FC = () => {
|
||||
);
|
||||
};
|
||||
|
||||
// Operator-image override. A plain string knob that used to ride along inside
|
||||
// worker_config; it now lives directly on k8s_options.
|
||||
const OperatorImageForm: React.FC = () => {
|
||||
const intl = useIntl();
|
||||
|
||||
@@ -273,7 +132,7 @@ const K8sPodSpec: React.FC<{
|
||||
<GpuInstanceOptionsForm initialValue={initialGpuInstanceOptions} />
|
||||
<NamespaceForm />
|
||||
<K8SVolumeMount action={action}></K8SVolumeMount>
|
||||
<ImageCredentialsForm />
|
||||
<ImageCredential />
|
||||
<NodeSelectorForm />
|
||||
<OperatorImageForm />
|
||||
</>
|
||||
|
||||
@@ -169,7 +169,9 @@ const VolumeMountsForm: React.FC<{ action: PageActionType }> = ({ action }) => {
|
||||
open={collapseKey.has(name)}
|
||||
onToggle={(open: boolean) => onToggle(open, name)}
|
||||
styles={{
|
||||
body: collapseKey.has(name) ? { padding: 16 } : {},
|
||||
body: collapseKey.has(name)
|
||||
? { paddingBlock: '16px 0', paddingInline: 16 }
|
||||
: {},
|
||||
content: { paddingTop: 0 },
|
||||
header: {
|
||||
backgroundColor: 'unset'
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { createContext, useContext } from 'react';
|
||||
import { ClusterListItem } from './types';
|
||||
|
||||
// for cluster form
|
||||
interface FormContextProps {
|
||||
currentData?: ClusterListItem;
|
||||
submitAttempted?: boolean;
|
||||
}
|
||||
|
||||
export const FormContext = createContext<FormContextProps>({});
|
||||
|
||||
export const useFormContext = () => useContext(FormContext);
|
||||
@@ -0,0 +1,11 @@
|
||||
.container {
|
||||
:global(.item-container) {
|
||||
align-items: flex-start;
|
||||
|
||||
&:nth-child(2) {
|
||||
:global(.btn) {
|
||||
margin-top: 16px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ const Basic = ({ action }: { action: string }) => {
|
||||
</Form.Item>
|
||||
<Form.Item<FormData> name="displayName">
|
||||
<CInput.Input
|
||||
trim={false}
|
||||
label={intl.formatMessage({ id: 'common.table.displayName' })}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { ModelFileFormData as FormData } from '@/pages/resources/config/types';
|
||||
import {
|
||||
CheckboxField,
|
||||
Input as CInput,
|
||||
Cascader as SealCascader,
|
||||
Select as SealSelect,
|
||||
@@ -204,6 +203,13 @@ const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
|
||||
root: 'cascader-popup-wrapper gpu-selector'
|
||||
}
|
||||
}}
|
||||
styles={{
|
||||
popup: {
|
||||
listItem: {
|
||||
maxWidth: '100%'
|
||||
}
|
||||
}
|
||||
}}
|
||||
maxTagCount={1}
|
||||
label={intl.formatMessage({ id: 'resources.worker' })}
|
||||
options={workerOptions}
|
||||
@@ -241,20 +247,6 @@ const TargetForm: React.FC<TargetFormProps> = forwardRef((props, ref) => {
|
||||
></CInput.Input>
|
||||
</Form.Item>
|
||||
)}
|
||||
{source === modelSourceMap.local_path_value && (
|
||||
<Form.Item<FormData>
|
||||
name="is_lora"
|
||||
key="is_lora"
|
||||
valuePropName="checked"
|
||||
noStyle
|
||||
>
|
||||
<CheckboxField
|
||||
label={intl.formatMessage({
|
||||
id: 'resources.modelfiles.form.isLora'
|
||||
})}
|
||||
></CheckboxField>
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user