diff --git a/src/pages/cluster-management/components/cluster-form.tsx b/src/pages/cluster-management/components/cluster-form.tsx
index e84682ec..a8122891 100644
--- a/src/pages/cluster-management/components/cluster-form.tsx
+++ b/src/pages/cluster-management/components/cluster-form.tsx
@@ -14,6 +14,7 @@ import {
} from '../config/types';
import AdvanceConfig from '../step-forms/advance-config';
import CloudProvider from './cloud-provider-form';
+import K8SVolumeMount from './k8s-volume-mount';
type AddModalProps = {
action: PageActionType;
@@ -60,7 +61,17 @@ const ClusterForm: React.FC
= forwardRef(
useEffect(() => {
if (currentData) {
- form.setFieldsValue(currentData);
+ const volumeMounts = currentData?.k8s_volume_mounts || [];
+ const realVolumeList = (volumeMounts || []).map(
+ (item: any, index: number) => ({
+ ...item,
+ sourceType: Object.keys(item.volumeSource || {})[0] || 'hostPath'
+ })
+ );
+ form.setFieldsValue({
+ ...currentData,
+ k8s_volume_mounts: realVolumeList
+ });
}
}, [currentData]);
@@ -151,6 +162,7 @@ const ClusterForm: React.FC = forwardRef(
credentialList={credentialList}
>
)}
+
name="description"
rules={[{ required: false }]}
@@ -161,6 +173,12 @@ const ClusterForm: React.FC = forwardRef(
label={intl.formatMessage({ id: 'common.table.description' })}
>
+ {provider === ProviderValueMap.Kubernetes && (
+
+ )}
= ({ action }) => {
+ const form = Form.useFormInstance();
+ const intl = useIntl();
+ const { getRuleMessage } = useAppUtils();
+
+ const [collapseKey, setCollapseKey] = useState>(
+ new Set([0])
+ );
+
+ const volumeList = [
+ {
+ name: 'volume-1',
+ mountPath: '',
+ readOnly: false,
+ sourceType: 'hostPath',
+ volumeSource: {
+ hostPath: {
+ path: '',
+ type: 'DirectoryOrCreate'
+ }
+ }
+ }
+ ];
+
+ useEffect(() => {
+ if (action === PageAction.CREATE) {
+ form.setFieldValue('k8s_volume_mounts', volumeList);
+ }
+ }, [action]);
+
+ const onToggle = (open: boolean, key: number) => {
+ setCollapseKey(open ? new Set([key]) : new Set());
+ };
+
+ const handleAdd = async () => {
+ try {
+ await form.validateFields(['k8s_volume_mounts'], {
+ recursive: true
+ });
+
+ const list = form.getFieldValue('k8s_volume_mounts') || [];
+
+ form.setFieldValue('k8s_volume_mounts', [
+ ...list,
+ {
+ name: `volume-${list.length + 1}`,
+ mountPath: '',
+ readOnly: false,
+ sourceType: 'hostPath',
+ volumeSource: {
+ hostPath: {
+ path: '',
+ type: 'DirectoryOrCreate'
+ }
+ }
+ }
+ ]);
+
+ setTimeout(() => {
+ setCollapseKey(new Set([list.length]));
+ }, 100);
+ } catch (e: any) {
+ const errorIndex = e?.errorFields?.[0]?.name?.[1];
+ if (typeof errorIndex === 'number') {
+ setCollapseKey(new Set([errorIndex]));
+ }
+ }
+ };
+
+ const handleSourceChange = (value: string, index: number) => {
+ const list = form.getFieldValue('k8s_volume_mounts') || [];
+
+ const updated = list.map((item: any, i: number) => {
+ if (i !== index) return item;
+
+ let volumeSource: any = {};
+
+ if (value === 'hostPath') {
+ volumeSource = {
+ hostPath: { path: '', type: 'DirectoryOrCreate' }
+ };
+ } else if (value === 'pvc') {
+ volumeSource = {
+ persistentVolumeClaim: { claimName: '', readOnly: false }
+ };
+ } else if (value === 'configMap') {
+ volumeSource = {
+ configMap: { name: '', optional: false }
+ };
+ }
+
+ return {
+ ...item,
+ sourceType: value,
+ volumeSource
+ };
+ });
+
+ form.setFieldValue('k8s_volume_mounts', updated);
+ };
+
+ return (
+ <>
+
+
+
{intl.formatMessage({ id: 'clusters.volume.title' })}
+
+
+
+
+
+ {(fields, { remove }) => {
+ const list = form.getFieldValue('k8s_volume_mounts') || [];
+
+ return fields.map(({ name }) => {
+ const item = list[name] || {};
+
+ return (
+
+
onToggle(open, name)}
+ styles={{
+ body: collapseKey.has(name) ? { padding: 16 } : {},
+ content: { paddingTop: 0 },
+ header: {
+ backgroundColor: 'unset'
+ }
+ }}
+ title={
+
+ }
+ right={
+
+ }
+ >
+
+
+
+
+ {/* Mount Path */}
+
+
+
+
+
+
+ {/* ReadOnly */}
+
+
+
+
+
+ {/* Source Type */}
+
+ handleSourceChange(value, name)}
+ options={sourceTypeOptions}
+ value={item.sourceType}
+ >
+
+
+ {/* Dynamic Source Form */}
+ {item.sourceType === 'hostPath' && (
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ {item.sourceType === 'pvc' && (
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ {item.sourceType === 'configMap' && (
+
+
+
+
+
+
+
+
+
+
+
+ )}
+
+
+ );
+ });
+ }}
+
+
+ >
+ );
+};
+
+export default VolumeMountsForm;
diff --git a/src/pages/cluster-management/config/index.ts b/src/pages/cluster-management/config/index.ts
index 542c4f6d..6e7eeac5 100644
--- a/src/pages/cluster-management/config/index.ts
+++ b/src/pages/cluster-management/config/index.ts
@@ -97,3 +97,55 @@ export const CloudOptionItems = [
key: 'volumes'
}
];
+
+export const hostTypeOptions = [
+ {
+ label: 'clusters.volume.hostPath.type.directory',
+ locale: true,
+ value: 'Directory'
+ },
+ {
+ label: 'clusters.volume.hostPath.type.directoryOrCreate',
+ locale: true,
+ value: 'DirectoryOrCreate'
+ },
+ {
+ label: 'clusters.volume.hostPath.type.file',
+ locale: true,
+ value: 'File'
+ },
+ {
+ label: 'clusters.volume.hostPath.type.fileOrCreate',
+ locale: true,
+ value: 'FileOrCreate'
+ },
+ {
+ label: 'clusters.volume.hostPath.type.socket',
+ locale: true,
+ value: 'Socket'
+ },
+ {
+ label: 'clusters.volume.hostPath.type.charDevice',
+ locale: true,
+ value: 'CharDevice'
+ },
+ {
+ label: 'clusters.volume.hostPath.type.blockDevice',
+ locale: true,
+ value: 'BlockDevice'
+ }
+];
+
+export const sourceTypeOptions = [
+ {
+ label: 'clusters.volume.sourceType.hostPath',
+ locale: true,
+ value: 'hostPath'
+ },
+ { label: 'clusters.volume.sourceType.pvc', locale: true, value: 'pvc' },
+ {
+ label: 'clusters.volume.sourceType.configMap',
+ locale: true,
+ value: 'configMap'
+ }
+];
diff --git a/src/pages/cluster-management/config/types.ts b/src/pages/cluster-management/config/types.ts
index 7c752a72..ab05735e 100644
--- a/src/pages/cluster-management/config/types.ts
+++ b/src/pages/cluster-management/config/types.ts
@@ -48,6 +48,25 @@ export interface NodePoolListItem extends NodePoolFormData {
updated_at: string;
cluster_id: number;
}
+export interface VolumeMount {
+ name: string;
+ mountPath: string;
+ readOnly: boolean;
+ volumeSource: {
+ hostPath: {
+ path: string;
+ type: string;
+ };
+ persistentVolumeClaim: {
+ claimName: string;
+ readOnly: boolean;
+ };
+ configMap: {
+ name: string;
+ optional: boolean;
+ };
+ };
+}
export interface ClusterListItem {
name: string;
@@ -68,6 +87,7 @@ export interface ClusterListItem {
state: ClusterStatusType;
state_message: string;
worker_pools: NodePoolListItem[];
+ k8s_volume_mounts?: VolumeMount[];
}
export interface ClusterFormData {
@@ -81,6 +101,7 @@ export interface ClusterFormData {
server_url?: string;
worker_config?: Record;
worker_pools?: NodePoolFormData[];
+ k8s_volume_mounts?: VolumeMount[];
}
export interface SystemConfig {
diff --git a/src/pages/cluster-management/step-forms/basic-form.tsx b/src/pages/cluster-management/step-forms/basic-form.tsx
index d750b580..2b538528 100644
--- a/src/pages/cluster-management/step-forms/basic-form.tsx
+++ b/src/pages/cluster-management/step-forms/basic-form.tsx
@@ -1,20 +1,8 @@
import { PageActionType } from '@/config/types';
-import { useIntl } from '@umijs/max';
import { forwardRef, useImperativeHandle, useRef } from 'react';
-import styled from 'styled-components';
import ClusterForm from '../components/cluster-form';
import { ProviderType } from '../config';
-const Title = styled.span`
- display: flex;
- align-items: center;
- justify-content: space-between;
- font-weight: 600;
- font-size: 14px;
- .text {
- font-size: 20px;
- }
-`;
interface BasicFormProps {
provider: ProviderType;
action: PageActionType;
@@ -23,7 +11,6 @@ interface BasicFormProps {
}
const BasicForm = forwardRef((props: BasicFormProps, ref) => {
- const intl = useIntl();
const { provider, credentialList, action, currentData } = props;
const formRef = useRef(null);
@@ -38,25 +25,14 @@ const BasicForm = forwardRef((props: BasicFormProps, ref) => {
}));
return (
-
- {/*
- {intl.formatMessage({ id: 'clusters.create.configBasic' })}
-
- }
- marginTop={0}
- > */}
-
-
+
);
});