mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-09-11 22:09:44 +00:00
feat(datasource): Database connection Renewal (#2359)
Co-authored-by: aries_ckt <916701291@qq.com> Co-authored-by: Fangyin Cheng <staneyffer@gmail.com> Co-authored-by: ‘yanzhiyonggit config --global user.email git config --global user.name ‘yanzhiyong <zhiyong.yan@clife.cn>
This commit is contained in:
138
web/components/common/configurable-form.tsx
Normal file
138
web/components/common/configurable-form.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
import { ConfigurableParams } from '@/types/common';
|
||||
import { Checkbox, Form, FormInstance, Input, InputNumber, Select } from 'antd';
|
||||
import { useEffect } from 'react';
|
||||
import NestedFormFields from './nested-form-fields';
|
||||
|
||||
interface ParamValues {
|
||||
[key: string]: string | number | boolean | Record<string, any>;
|
||||
}
|
||||
|
||||
function ConfigurableForm({ params, form }: { params: Array<ConfigurableParams> | null; form: FormInstance<any> }) {
|
||||
useEffect(() => {
|
||||
if (params) {
|
||||
const initialValues: ParamValues = {};
|
||||
params.forEach(param => {
|
||||
if (!param.nested_fields) {
|
||||
// Only set default value when the field has not been modified by the user
|
||||
const currentValue = form.getFieldValue(param.param_name);
|
||||
if (currentValue === undefined) {
|
||||
initialValues[param.param_name] = param.default_value;
|
||||
}
|
||||
}
|
||||
});
|
||||
form.setFieldsValue(initialValues);
|
||||
}
|
||||
}, [params, form]);
|
||||
|
||||
if (!params || params?.length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Transform data structure before form submission
|
||||
const normalizeFormValues = (values: any) => {
|
||||
const normalized = { ...values };
|
||||
params?.forEach(param => {
|
||||
if (param.nested_fields && normalized[param.param_name]) {
|
||||
const nestedValue = normalized[param.param_name];
|
||||
if (nestedValue.type) {
|
||||
// Keep all field values instead of just type
|
||||
const nestedFields = param.nested_fields[nestedValue.type] || [];
|
||||
const fieldValues = {};
|
||||
nestedFields.forEach(field => {
|
||||
if (nestedValue[field.param_name] !== undefined) {
|
||||
fieldValues[field.param_name] = nestedValue[field.param_name];
|
||||
}
|
||||
});
|
||||
|
||||
normalized[param.param_name] = {
|
||||
type: nestedValue.type,
|
||||
...fieldValues,
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
return normalized;
|
||||
};
|
||||
|
||||
// Override the original submit method of the form
|
||||
const originalSubmit = form.submit;
|
||||
form.submit = () => {
|
||||
const values = form.getFieldsValue();
|
||||
const normalizedValues = normalizeFormValues(values);
|
||||
form.setFieldsValue(normalizedValues);
|
||||
originalSubmit.call(form);
|
||||
};
|
||||
|
||||
function renderItem(param: ConfigurableParams) {
|
||||
if (param.nested_fields) {
|
||||
return (
|
||||
<NestedFormFields
|
||||
parentName={param.param_name}
|
||||
fields={param.nested_fields as Record<string, ConfigurableParams[]>}
|
||||
form={form}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const type = param.param_type.toLowerCase();
|
||||
const isFixed = param.ext_metadata?.tags?.includes('fixed');
|
||||
const isPrivacy = param.ext_metadata?.tags?.includes('privacy');
|
||||
|
||||
if (type === 'str' || type === 'string') {
|
||||
// Handle dropdown selection box
|
||||
if (param.valid_values) {
|
||||
return (
|
||||
<Select disabled={isFixed}>
|
||||
{param.valid_values.map(value => (
|
||||
<Select.Option key={value} value={value}>
|
||||
{value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
|
||||
// Handle password input box
|
||||
if (isPrivacy) {
|
||||
return <Input.Password disabled={isFixed} autoComplete='new-password' placeholder='请输入密码' />;
|
||||
}
|
||||
|
||||
// Handle normal text input box
|
||||
return <Input disabled={isFixed} />;
|
||||
}
|
||||
|
||||
if (type === 'int' || type === 'integer' || type === 'number' || type === 'float') {
|
||||
return <InputNumber className='w-full' disabled={isFixed} />;
|
||||
}
|
||||
|
||||
if (type === 'bool' || type === 'boolean') {
|
||||
return <Checkbox disabled={isFixed} />;
|
||||
}
|
||||
|
||||
return <Input disabled={isFixed} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='space-y-4'>
|
||||
{params?.map((param: ConfigurableParams) => (
|
||||
<Form.Item
|
||||
key={param.param_name}
|
||||
label={<p className='whitespace-normal overflow-wrap-break-word'>{param.label || param.param_name}</p>}
|
||||
name={param.param_name}
|
||||
initialValue={param.default_value}
|
||||
valuePropName={
|
||||
param.param_type.toLowerCase() === 'bool' || param.param_type.toLowerCase() === 'boolean'
|
||||
? 'checked'
|
||||
: 'value'
|
||||
}
|
||||
tooltip={param.description}
|
||||
rules={param.required ? [{ required: true, message: `Please input ${param.param_name}` }] : []}
|
||||
>
|
||||
{renderItem(param)}
|
||||
</Form.Item>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ConfigurableForm;
|
105
web/components/common/nested-form-fields.tsx
Normal file
105
web/components/common/nested-form-fields.tsx
Normal file
@@ -0,0 +1,105 @@
|
||||
import { ConfigurableParams } from '@/types/common';
|
||||
import { Checkbox, Form, FormInstance, Input, InputNumber, Select } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
interface NestedFormFieldsProps {
|
||||
parentName: string;
|
||||
fields: Record<string, ConfigurableParams[]>;
|
||||
form: FormInstance;
|
||||
}
|
||||
const NestedFormFields: React.FC<NestedFormFieldsProps> = ({ parentName, fields, form }) => {
|
||||
const [selectedType, setSelectedType] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const currentValue = form.getFieldValue(parentName);
|
||||
if (currentValue?.type && !selectedType) {
|
||||
setSelectedType(currentValue.type);
|
||||
}
|
||||
}, [form, parentName]);
|
||||
|
||||
const handleTypeChange = (value: string) => {
|
||||
setSelectedType(value);
|
||||
|
||||
// Get all field configurations for the current type
|
||||
const typeFields = fields[value] || [];
|
||||
|
||||
// Create an object containing default values for all fields
|
||||
const defaultValues = {
|
||||
type: value,
|
||||
};
|
||||
|
||||
// Set default values for each field
|
||||
typeFields.forEach(field => {
|
||||
defaultValues[field.param_name] = field.default_value;
|
||||
});
|
||||
|
||||
// Set the entire object as the value of the form field
|
||||
form.setFieldsValue({
|
||||
[parentName]: defaultValues,
|
||||
});
|
||||
};
|
||||
|
||||
const renderFormItem = (param: ConfigurableParams) => {
|
||||
const type = param.param_type.toLowerCase();
|
||||
// Use the complete field path
|
||||
const fieldPath = [parentName, param.param_name];
|
||||
|
||||
let control;
|
||||
if (type === 'str' || type === 'string') {
|
||||
if (param.valid_values) {
|
||||
control = (
|
||||
<Select>
|
||||
{param.valid_values.map(value => (
|
||||
<Select.Option key={value} value={value}>
|
||||
{value}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
);
|
||||
} else {
|
||||
control = <Input />;
|
||||
}
|
||||
} else if (type === 'int' || type === 'integer' || type === 'number' || type === 'float') {
|
||||
control = <InputNumber className='w-full' />;
|
||||
} else if (type === 'bool' || type === 'boolean') {
|
||||
control = <Checkbox />;
|
||||
} else {
|
||||
control = <Input />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Form.Item
|
||||
key={param.param_name}
|
||||
label={param.label || param.param_name}
|
||||
name={fieldPath}
|
||||
valuePropName={type === 'bool' || type === 'boolean' ? 'checked' : 'value'}
|
||||
tooltip={param.description}
|
||||
rules={selectedType && param.required ? [{ required: true, message: `Please input ${param.param_name}` }] : []}
|
||||
>
|
||||
{control}
|
||||
</Form.Item>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='space-y-4 border rounded-md p-4'>
|
||||
<Form.Item label='Type' name={[parentName, 'type']}>
|
||||
<Select onChange={handleTypeChange} placeholder='Select a type'>
|
||||
{Object.keys(fields).map(type => (
|
||||
<Select.Option key={type} value={type}>
|
||||
{type}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
{selectedType && fields[selectedType] && (
|
||||
<div className='mt-4'>
|
||||
<h4 className='mb-4 text-base font-medium'>{selectedType} Configuration</h4>
|
||||
<div className='space-y-4'>{fields[selectedType].map(param => renderFormItem(param))}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default NestedFormFields;
|
Reference in New Issue
Block a user