chore: form tabs active change

This commit is contained in:
jialin
2026-02-12 12:20:05 +08:00
parent 07664b5f22
commit 978d327bcd
10 changed files with 175 additions and 43 deletions
+2 -1
View File
@@ -275,5 +275,6 @@ export default {
'common.button.disable': 'Disable',
'common.status.enabled': 'Enabled',
'common.status.disabled': 'Disabled',
'common.button.duplicate': 'Duplicate'
'common.button.duplicate': 'Duplicate',
'common.option.other': 'Other'
};
+4 -2
View File
@@ -275,7 +275,8 @@ export default {
'common.button.disable': 'Disable',
'common.status.enabled': 'Enabled',
'common.status.disabled': 'Disabled',
'common.button.duplicate': 'Duplicate'
'common.button.duplicate': 'Duplicate',
'common.option.other': 'Other'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -315,5 +316,6 @@ export default {
// 34. 'common.button.disable': 'Disable',
// 35. 'common.status.enabled': 'Enabled',
// 36. 'common.status.disabled': 'Disabled'
// 33. 'common.button.duplicate': 'Duplicate'
// 33. 'common.button.duplicate': 'Duplicate',
// 34. 'common.option.other': 'Other'
// ========== End of To-Do List ==========
+2 -1
View File
@@ -274,7 +274,8 @@ export default {
'common.button.disable': 'Disable',
'common.status.enabled': 'Enabled',
'common.status.disabled': 'Disabled',
'common.button.duplicate': 'Duplicate'
'common.button.duplicate': 'Duplicate',
'common.option.other': 'Другое'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
+2 -1
View File
@@ -267,5 +267,6 @@ export default {
'common.button.disable': '禁用',
'common.status.enabled': '已启用',
'common.status.disabled': '未启用',
'common.button.duplicate': '复制'
'common.button.duplicate': '复制',
'common.option.other': '其它'
};
@@ -33,9 +33,10 @@ const CollapsePanel: React.FC<{
items: CollapseProps['items'];
activeKey: string | string[];
accordion?: boolean;
defaultActiveKey?: string | string[];
onChange?: (key: string | string[]) => void;
styles?: Record<string, React.CSSProperties>;
}> = ({ items, activeKey, accordion, onChange, styles }) => {
}> = ({ items, activeKey, accordion, defaultActiveKey, onChange, styles }) => {
return (
<CollapseInner
expandIconPlacement="start"
@@ -43,6 +44,7 @@ const CollapsePanel: React.FC<{
ghost
accordion={accordion}
activeKey={activeKey}
defaultActiveKey={defaultActiveKey}
onChange={onChange}
destroyOnHidden={false}
styles={{
@@ -0,0 +1,44 @@
interface FinishFailedOptions {
requiredFields: {
[tab: string]: {
sort: number;
fields: string[];
};
};
onTargetChange: (key: string) => void;
updateActiveKey: (key: string[]) => void;
}
const useFinishFailed = (options: FinishFailedOptions) => {
const { requiredFields, onTargetChange, updateActiveKey } = options;
const handleOnFinishFailed = (errorInfo: any) => {
const { errorFields } = errorInfo;
console.log('Finish failed:', errorInfo);
if (errorFields && errorFields.length > 0) {
const collapseKeys: { sort: number; key: string }[] = [];
const names = errorFields.map((item: any) => item.name[0]);
Object.entries(requiredFields).forEach(([tab, { fields, sort }]) => {
const hasError = fields.some((field: string) => names.includes(field));
if (hasError) {
collapseKeys.push({ sort, key: tab });
}
});
if (collapseKeys.length > 0) {
const keys = collapseKeys
.sort((a, b) => a.sort - b.sort)
.map((item) => item.key);
updateActiveKey(keys);
onTargetChange(collapseKeys[0].key);
}
}
};
return {
handleOnFinishFailed
};
};
export default useFinishFailed;
@@ -0,0 +1,39 @@
import { useState } from 'react';
const useScrollActiveChange = (options: {
initalActiveKeys: string[];
initialCollapseKeys?: string[];
}) => {
const [activeKey, setActiveKey] = useState<string[]>(
options.initalActiveKeys
);
const [collapseKeys, setCollapseKeys] = useState<string[]>(
options.initialCollapseKeys || options.initalActiveKeys
);
const handleActiveChange = (key: string[]) => {
setActiveKey(key);
setCollapseKeys(key);
};
const handleOnCollapseChange = (keys: string | string[]) => {
const keysArray = Array.isArray(keys) ? keys : [keys];
setActiveKey(keysArray);
setCollapseKeys(keysArray);
};
const updateActiveKey = (keys: string[]) => {
setActiveKey((prev: string[]) => [...new Set([...prev, ...keys])]);
setCollapseKeys((prev) => [...new Set([...prev, ...keys])]);
};
return {
activeKey,
collapseKeys,
handleActiveChange,
handleOnCollapseChange,
updateActiveKey
};
};
export default useScrollActiveChange;
+33 -15
View File
@@ -4,17 +4,13 @@ import { PageActionType } from '@/config/types';
import CollapsePanel from '@/pages/_components/collapse-panel';
import { useWrapperContext } from '@/pages/_components/column-wrapper/use-wrapper-context';
import ScrollSpyTabs from '@/pages/_components/scroll-spy-tabs';
import useFinishFailed from '@/pages/_components/scroll-spy-tabs/use-finish-failed';
import useScrollActiveChange from '@/pages/_components/scroll-spy-tabs/use-scroll-active-change';
import { json2Yaml, yaml2Json } from '@/pages/backends/config';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import _ from 'lodash';
import {
forwardRef,
useEffect,
useImperativeHandle,
useRef,
useState
} from 'react';
import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
import FormContext from '../config/form-context';
import { FormData, MaasProviderItem as ListItem } from '../config/types';
import AdvanceConfig from './advance-config';
@@ -31,18 +27,37 @@ interface ProviderFormProps {
const TABKeysMap = {
BASIC: 'basic',
SUPPORTEDMODELS: 'supportedModels',
CUSTOMCONFIG: 'customConfig',
ADVANCED: 'advanced'
};
const requiredFields = {
[TABKeysMap.BASIC]: {
sort: 1,
fields: ['name', 'config.type', 'api_key']
},
[TABKeysMap.SUPPORTEDMODELS]: {
sort: 2,
fields: ['models']
}
};
const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
const { action, currentData, onFinish } = props;
const intl = useIntl();
const [form] = Form.useForm();
const { getScrollElementScrollableHeight } = useWrapperContext();
const [activeKey, setActiveKey] = useState<string[]>([TABKeysMap.BASIC]);
const scrollTabsRef = useRef<any>(null);
const advanceRef = useRef<any>(null);
const {
activeKey,
collapseKeys,
handleActiveChange,
handleOnCollapseChange,
updateActiveKey
} = useScrollActiveChange({
initalActiveKeys: [TABKeysMap.BASIC],
initialCollapseKeys: [TABKeysMap.SUPPORTEDMODELS]
});
const segmentOptions = [
{
@@ -96,13 +111,15 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
onFinish(data);
};
const handleActiveChange = (key: string[]) => {
setActiveKey(key);
const onTargetChange = (key: string) => {
scrollTabsRef.current?.handleTargetChange(key);
};
const handleOnCollapseChange = (keys: string | string[]) => {
setActiveKey(Array.isArray(keys) ? keys : [keys]);
};
const { handleOnFinishFailed } = useFinishFailed({
requiredFields,
onTargetChange,
updateActiveKey
});
useImperativeHandle(ref, () => ({
submit: () => {
@@ -152,6 +169,7 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
<Form
form={form}
onFinish={handleOnFinish}
onFinishFailed={handleOnFinishFailed}
initialValues={{
proxy_enabled: false,
proxy_url: '',
@@ -160,7 +178,7 @@ const ProviderForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
>
<Basic />
<CollapsePanel
activeKey={activeKey}
activeKey={collapseKeys}
accordion={false}
onChange={handleOnCollapseChange}
items={[
+9 -2
View File
@@ -38,13 +38,20 @@ const Basic = () => {
})}
rules={[
{
required: false,
required: true,
message: getRuleMessage('select', 'models.form.categories')
}
]}
>
<CategorySelect
options={[{ label: '', value: '' }, ...categoryOptions]}
required={true}
options={[
...categoryOptions,
{
label: intl.formatMessage({ id: 'common.option.other' }),
value: 'other'
}
]}
label={intl.formatMessage({ id: 'models.form.categories' })}
></CategorySelect>
</Form.Item>
+37 -20
View File
@@ -4,17 +4,13 @@ import { PageActionType } from '@/config/types';
import CollapsePanel from '@/pages/_components/collapse-panel';
import { useWrapperContext } from '@/pages/_components/column-wrapper/use-wrapper-context';
import ScrollSpyTabs from '@/pages/_components/scroll-spy-tabs';
import useFinishFailed from '@/pages/_components/scroll-spy-tabs/use-finish-failed';
import useScrollActiveChange from '@/pages/_components/scroll-spy-tabs/use-scroll-active-change';
import { modelCategoriesMap } from '@/pages/llmodels/config';
import { useIntl } from '@umijs/max';
import { Form } from 'antd';
import _ from 'lodash';
import {
forwardRef,
useEffect,
useImperativeHandle,
useRef,
useState
} from 'react';
import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
import FormContext from '../config/form-context';
import { FormData, RouteItem as ListItem } from '../config/types';
import useEditTargets from '../hooks/use-edit-targets';
@@ -40,9 +36,18 @@ interface ProviderFormProps {
const TABKeysMap = {
BASIC: 'basic',
METADATA: 'metadata',
TARGETS: 'targets',
ADVANCED: 'advanced'
TARGETS: 'targets'
};
const requiredFields = {
[TABKeysMap.BASIC]: {
sort: 1,
fields: ['name']
},
[TABKeysMap.TARGETS]: {
sort: 2,
fields: ['targets']
}
};
const AccessForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
@@ -50,11 +55,20 @@ const AccessForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
const { action, realAction, currentData, open, onFinish, onFallbackChange } =
props;
const { getScrollElementScrollableHeight } = useWrapperContext();
const [activeKey, setActiveKey] = useState<string[]>([TABKeysMap.BASIC]);
const [form] = Form.useForm();
const scrollTabsRef = useRef<any>(null);
const targetsRef = useRef<any>(null);
const { generateTargetData, fetchTargets } = useEditTargets();
const {
activeKey,
collapseKeys,
handleActiveChange,
handleOnCollapseChange,
updateActiveKey
} = useScrollActiveChange({
initalActiveKeys: [TABKeysMap.BASIC],
initialCollapseKeys: [TABKeysMap.TARGETS]
});
const segmentOptions = [
{
@@ -71,10 +85,6 @@ const AccessForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
}
];
const handleActiveChange = (key: string[]) => {
setActiveKey(key);
};
const formatTargets = (values: FormData) => {
let targetList = [...(values.targets || [])];
let fallbackTarget = values.fallback_target;
@@ -125,10 +135,6 @@ const AccessForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
onFinish(data);
};
const handleOnCollapseChange = (keys: string | string[]) => {
setActiveKey(Array.isArray(keys) ? keys : [keys]);
};
// init form values
useEffect(() => {
if (!open) {
@@ -194,6 +200,16 @@ const AccessForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
}
}, [action, currentData, form, open, realAction]);
const onTargetChange = (key: string) => {
scrollTabsRef.current?.handleTargetChange(key);
};
const { handleOnFinishFailed } = useFinishFailed({
requiredFields,
onTargetChange,
updateActiveKey
});
useImperativeHandle(ref, () => ({
submit: () => {
form.submit();
@@ -222,6 +238,7 @@ const AccessForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
<Form
form={form}
onFinish={handleOnFinish}
onFinishFailed={handleOnFinishFailed}
initialValues={{
categories: [modelCategoriesMap.llm],
meta: {}
@@ -229,7 +246,7 @@ const AccessForm: React.FC<ProviderFormProps> = forwardRef((props, ref) => {
>
<Basic />
<CollapsePanel
activeKey={activeKey}
activeKey={collapseKeys}
accordion={false}
onChange={handleOnCollapseChange}
items={[