@@ -68,6 +68,8 @@ import {
6868 IconCode ,
6969 IconGlobe ,
7070 IconBolt ,
71+ IconChevronUp ,
72+ IconChevronDown ,
7173} from '@douyinfe/semi-icons' ;
7274
7375const { Text, Title } = Typography ;
@@ -206,6 +208,27 @@ const EditChannelModal = (props) => {
206208 keyData : '' ,
207209 } ) ;
208210
211+ // 专门的2FA验证状态(用于TwoFactorAuthModal)
212+ const [ show2FAVerifyModal , setShow2FAVerifyModal ] = useState ( false ) ;
213+ const [ verifyCode , setVerifyCode ] = useState ( '' ) ;
214+ const [ verifyLoading , setVerifyLoading ] = useState ( false ) ;
215+
216+ // 表单块导航相关状态
217+ const formSectionRefs = useRef ( {
218+ basicInfo : null ,
219+ apiConfig : null ,
220+ modelConfig : null ,
221+ advancedSettings : null ,
222+ channelExtraSettings : null ,
223+ } ) ;
224+ const [ currentSectionIndex , setCurrentSectionIndex ] = useState ( 0 ) ;
225+ const formSections = [ 'basicInfo' , 'apiConfig' , 'modelConfig' , 'advancedSettings' , 'channelExtraSettings' ] ;
226+ const formContainerRef = useRef ( null ) ;
227+
228+ // 2FA状态更新辅助函数
229+ const updateTwoFAState = ( updates ) => {
230+ setTwoFAState ( ( prev ) => ( { ...prev , ...updates } ) ) ;
231+ } ;
209232 // 使用通用安全验证 Hook
210233 const {
211234 isModalVisible,
@@ -245,6 +268,44 @@ const EditChannelModal = (props) => {
245268 } ) ;
246269 } ;
247270
271+ // 重置2FA验证状态
272+ const reset2FAVerifyState = ( ) => {
273+ setShow2FAVerifyModal ( false ) ;
274+ setVerifyCode ( '' ) ;
275+ setVerifyLoading ( false ) ;
276+ } ;
277+
278+ // 表单导航功能
279+ const scrollToSection = ( sectionKey ) => {
280+ const sectionElement = formSectionRefs . current [ sectionKey ] ;
281+ if ( sectionElement ) {
282+ sectionElement . scrollIntoView ( {
283+ behavior : 'smooth' ,
284+ block : 'start' ,
285+ inline : 'nearest'
286+ } ) ;
287+ }
288+ } ;
289+
290+ const navigateToSection = ( direction ) => {
291+ const availableSections = formSections . filter ( section => {
292+ if ( section === 'apiConfig' ) {
293+ return showApiConfigCard ;
294+ }
295+ return true ;
296+ } ) ;
297+
298+ let newIndex ;
299+ if ( direction === 'up' ) {
300+ newIndex = currentSectionIndex > 0 ? currentSectionIndex - 1 : availableSections . length - 1 ;
301+ } else {
302+ newIndex = currentSectionIndex < availableSections . length - 1 ? currentSectionIndex + 1 : 0 ;
303+ }
304+
305+ setCurrentSectionIndex ( newIndex ) ;
306+ scrollToSection ( availableSections [ newIndex ] ) ;
307+ } ;
308+
248309 // 渠道额外设置状态
249310 const [ channelSettings , setChannelSettings ] = useState ( {
250311 force_format : false ,
@@ -729,6 +790,8 @@ const EditChannelModal = (props) => {
729790 fetchModelGroups ( ) ;
730791 // 重置手动输入模式状态
731792 setUseManualInput ( false ) ;
793+ // 重置导航状态
794+ setCurrentSectionIndex ( 0 ) ;
732795 } else {
733796 // 统一的模态框关闭重置逻辑
734797 resetModalState ( ) ;
@@ -1270,7 +1333,41 @@ const EditChannelModal = (props) => {
12701333 visible = { props . visible }
12711334 width = { isMobile ? '100%' : 600 }
12721335 footer = {
1273- < div className = 'flex justify-end bg-white' >
1336+ < div className = 'flex justify-between items-center bg-white' >
1337+ < div className = 'flex gap-2' >
1338+ < Button
1339+ size = 'small'
1340+ type = 'tertiary'
1341+ icon = { < IconChevronUp /> }
1342+ onClick = { ( ) => navigateToSection ( 'up' ) }
1343+ style = { {
1344+ borderRadius : '50%' ,
1345+ width : '32px' ,
1346+ height : '32px' ,
1347+ padding : 0 ,
1348+ display : 'flex' ,
1349+ alignItems : 'center' ,
1350+ justifyContent : 'center'
1351+ } }
1352+ title = { t ( '上一个表单块' ) }
1353+ />
1354+ < Button
1355+ size = 'small'
1356+ type = 'tertiary'
1357+ icon = { < IconChevronDown /> }
1358+ onClick = { ( ) => navigateToSection ( 'down' ) }
1359+ style = { {
1360+ borderRadius : '50%' ,
1361+ width : '32px' ,
1362+ height : '32px' ,
1363+ padding : 0 ,
1364+ display : 'flex' ,
1365+ alignItems : 'center' ,
1366+ justifyContent : 'center'
1367+ } }
1368+ title = { t ( '下一个表单块' ) }
1369+ />
1370+ </ div >
12741371 < Space >
12751372 < Button
12761373 theme = 'solid'
@@ -1301,10 +1398,14 @@ const EditChannelModal = (props) => {
13011398 >
13021399 { ( ) => (
13031400 < Spin spinning = { loading } >
1304- < div className = 'p-2' >
1305- < Card className = '!rounded-2xl shadow-sm border-0 mb-6' >
1306- { /* Header: Basic Info */ }
1307- < div className = 'flex items-center mb-2' >
1401+ < div
1402+ className = 'p-2'
1403+ ref = { formContainerRef }
1404+ >
1405+ < div ref = { el => formSectionRefs . current . basicInfo = el } >
1406+ < Card className = '!rounded-2xl shadow-sm border-0 mb-6' >
1407+ { /* Header: Basic Info */ }
1408+ < div className = 'flex items-center mb-2' >
13081409 < Avatar
13091410 size = 'small'
13101411 color = 'blue'
@@ -1778,13 +1879,15 @@ const EditChannelModal = (props) => {
17781879 }
17791880 />
17801881 ) }
1781- </ Card >
1882+ </ Card >
1883+ </ div >
17821884
17831885 { /* API Configuration Card */ }
17841886 { showApiConfigCard && (
1785- < Card className = '!rounded-2xl shadow-sm border-0 mb-6' >
1786- { /* Header: API Config */ }
1787- < div className = 'flex items-center mb-2' >
1887+ < div ref = { el => formSectionRefs . current . apiConfig = el } >
1888+ < Card className = '!rounded-2xl shadow-sm border-0 mb-6' >
1889+ { /* Header: API Config */ }
1890+ < div className = 'flex items-center mb-2' >
17881891 < Avatar
17891892 size = 'small'
17901893 color = 'green'
@@ -1995,13 +2098,15 @@ const EditChannelModal = (props) => {
19952098 />
19962099 </ div >
19972100 ) }
1998- </ Card >
2101+ </ Card >
2102+ </ div >
19992103 ) }
20002104
20012105 { /* Model Configuration Card */ }
2002- < Card className = '!rounded-2xl shadow-sm border-0 mb-6' >
2003- { /* Header: Model Config */ }
2004- < div className = 'flex items-center mb-2' >
2106+ < div ref = { el => formSectionRefs . current . modelConfig = el } >
2107+ < Card className = '!rounded-2xl shadow-sm border-0 mb-6' >
2108+ { /* Header: Model Config */ }
2109+ < div className = 'flex items-center mb-2' >
20052110 < Avatar
20062111 size = 'small'
20072112 color = 'purple'
@@ -2196,12 +2301,14 @@ const EditChannelModal = (props) => {
21962301 formApi = { formApiRef . current }
21972302 extraText = { t ( '键为请求中的模型名称,值为要替换的模型名称' ) }
21982303 />
2199- </ Card >
2304+ </ Card >
2305+ </ div >
22002306
22012307 { /* Advanced Settings Card */ }
2202- < Card className = '!rounded-2xl shadow-sm border-0 mb-6' >
2203- { /* Header: Advanced Settings */ }
2204- < div className = 'flex items-center mb-2' >
2308+ < div ref = { el => formSectionRefs . current . advancedSettings = el } >
2309+ < Card className = '!rounded-2xl shadow-sm border-0 mb-6' >
2310+ { /* Header: Advanced Settings */ }
2311+ < div className = 'flex items-center mb-2' >
22052312 < Avatar
22062313 size = 'small'
22072314 color = 'orange'
@@ -2414,6 +2521,8 @@ const EditChannelModal = (props) => {
24142521 '键为原状态码,值为要复写的状态码,仅影响本地判断' ,
24152522 ) }
24162523 />
2524+ </ Card >
2525+ </ div >
24172526
24182527 { /* 字段透传控制 - OpenAI 渠道 */ }
24192528 { inputs . type === 1 && (
@@ -2487,9 +2596,10 @@ const EditChannelModal = (props) => {
24872596 </ Card >
24882597
24892598 { /* Channel Extra Settings Card */ }
2490- < Card className = '!rounded-2xl shadow-sm border-0 mb-6' >
2491- { /* Header: Channel Extra Settings */ }
2492- < div className = 'flex items-center mb-2' >
2599+ < div ref = { el => formSectionRefs . current . channelExtraSettings = el } >
2600+ < Card className = '!rounded-2xl shadow-sm border-0 mb-6' >
2601+ { /* Header: Channel Extra Settings */ }
2602+ < div className = 'flex items-center mb-2' >
24932603 < Avatar
24942604 size = 'small'
24952605 color = 'violet'
@@ -2587,6 +2697,8 @@ const EditChannelModal = (props) => {
25872697 '如果用户请求中包含系统提示词,则使用此设置拼接到用户的系统提示词前面' ,
25882698 ) }
25892699 />
2700+ </ Card >
2701+ </ div >
25902702
25912703 </ Card >
25922704 < / d i v >
0 commit comments