diff --git a/src/locales/en-US/common.ts b/src/locales/en-US/common.ts index 3a928b51..ebf5a02e 100644 --- a/src/locales/en-US/common.ts +++ b/src/locales/en-US/common.ts @@ -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' }; diff --git a/src/locales/ja-JP/common.ts b/src/locales/ja-JP/common.ts index 0603fbec..d7237d6f 100644 --- a/src/locales/ja-JP/common.ts +++ b/src/locales/ja-JP/common.ts @@ -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 ========== diff --git a/src/locales/ru-RU/common.ts b/src/locales/ru-RU/common.ts index e68ebce2..b0bab020 100644 --- a/src/locales/ru-RU/common.ts +++ b/src/locales/ru-RU/common.ts @@ -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) ========== diff --git a/src/locales/zh-CN/common.ts b/src/locales/zh-CN/common.ts index 902cb84e..a7c408b7 100644 --- a/src/locales/zh-CN/common.ts +++ b/src/locales/zh-CN/common.ts @@ -267,5 +267,6 @@ export default { 'common.button.disable': '禁用', 'common.status.enabled': '已启用', 'common.status.disabled': '未启用', - 'common.button.duplicate': '复制' + 'common.button.duplicate': '复制', + 'common.option.other': '其它' }; diff --git a/src/pages/_components/collapse-panel/index.tsx b/src/pages/_components/collapse-panel/index.tsx index c1d7543e..4f5edd2d 100644 --- a/src/pages/_components/collapse-panel/index.tsx +++ b/src/pages/_components/collapse-panel/index.tsx @@ -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; -}> = ({ items, activeKey, accordion, onChange, styles }) => { +}> = ({ items, activeKey, accordion, defaultActiveKey, onChange, styles }) => { return ( 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; diff --git a/src/pages/_components/scroll-spy-tabs/use-scroll-active-change.ts b/src/pages/_components/scroll-spy-tabs/use-scroll-active-change.ts new file mode 100644 index 00000000..e37c7cdd --- /dev/null +++ b/src/pages/_components/scroll-spy-tabs/use-scroll-active-change.ts @@ -0,0 +1,39 @@ +import { useState } from 'react'; + +const useScrollActiveChange = (options: { + initalActiveKeys: string[]; + initialCollapseKeys?: string[]; +}) => { + const [activeKey, setActiveKey] = useState( + options.initalActiveKeys + ); + const [collapseKeys, setCollapseKeys] = useState( + 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; diff --git a/src/pages/maas-provider/forms/index.tsx b/src/pages/maas-provider/forms/index.tsx index 34f8a2af..52353887 100644 --- a/src/pages/maas-provider/forms/index.tsx +++ b/src/pages/maas-provider/forms/index.tsx @@ -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 = forwardRef((props, ref) => { const { action, currentData, onFinish } = props; const intl = useIntl(); const [form] = Form.useForm(); const { getScrollElementScrollableHeight } = useWrapperContext(); - const [activeKey, setActiveKey] = useState([TABKeysMap.BASIC]); const scrollTabsRef = useRef(null); const advanceRef = useRef(null); + const { + activeKey, + collapseKeys, + handleActiveChange, + handleOnCollapseChange, + updateActiveKey + } = useScrollActiveChange({ + initalActiveKeys: [TABKeysMap.BASIC], + initialCollapseKeys: [TABKeysMap.SUPPORTEDMODELS] + }); const segmentOptions = [ { @@ -96,13 +111,15 @@ const ProviderForm: React.FC = 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 = forwardRef((props, ref) => {
= forwardRef((props, ref) => { > { })} rules={[ { - required: false, + required: true, message: getRuleMessage('select', 'models.form.categories') } ]} > diff --git a/src/pages/model-routes/forms/index.tsx b/src/pages/model-routes/forms/index.tsx index d464fe75..2daf1175 100644 --- a/src/pages/model-routes/forms/index.tsx +++ b/src/pages/model-routes/forms/index.tsx @@ -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 = forwardRef((props, ref) => { @@ -50,11 +55,20 @@ const AccessForm: React.FC = forwardRef((props, ref) => { const { action, realAction, currentData, open, onFinish, onFallbackChange } = props; const { getScrollElementScrollableHeight } = useWrapperContext(); - const [activeKey, setActiveKey] = useState([TABKeysMap.BASIC]); const [form] = Form.useForm(); const scrollTabsRef = useRef(null); const targetsRef = useRef(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 = 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 = 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 = 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 = forwardRef((props, ref) => { = forwardRef((props, ref) => { >