feat: jump between section on channel edit page

This commit is contained in:
JoeyLearnsToCode
2025-09-19 18:11:47 +08:00
parent d77aa81f4f
commit a23955a025
@@ -66,6 +66,8 @@ import {
IconCode, IconCode,
IconGlobe, IconGlobe,
IconBolt, IconBolt,
IconChevronUp,
IconChevronDown,
} from '@douyinfe/semi-icons'; } from '@douyinfe/semi-icons';
const { Text, Title } = Typography; const { Text, Title } = Typography;
@@ -184,6 +186,18 @@ const EditChannelModal = (props) => {
const [verifyCode, setVerifyCode] = useState(''); const [verifyCode, setVerifyCode] = useState('');
const [verifyLoading, setVerifyLoading] = useState(false); const [verifyLoading, setVerifyLoading] = useState(false);
// 表单块导航相关状态
const formSectionRefs = useRef({
basicInfo: null,
apiConfig: null,
modelConfig: null,
advancedSettings: null,
channelExtraSettings: null,
});
const [currentSectionIndex, setCurrentSectionIndex] = useState(0);
const formSections = ['basicInfo', 'apiConfig', 'modelConfig', 'advancedSettings', 'channelExtraSettings'];
const formContainerRef = useRef(null);
// 2FA状态更新辅助函数 // 2FA状态更新辅助函数
const updateTwoFAState = (updates) => { const updateTwoFAState = (updates) => {
setTwoFAState((prev) => ({ ...prev, ...updates })); setTwoFAState((prev) => ({ ...prev, ...updates }));
@@ -207,6 +221,37 @@ const EditChannelModal = (props) => {
setVerifyLoading(false); setVerifyLoading(false);
}; };
// 表单导航功能
const scrollToSection = (sectionKey) => {
const sectionElement = formSectionRefs.current[sectionKey];
if (sectionElement) {
sectionElement.scrollIntoView({
behavior: 'smooth',
block: 'start',
inline: 'nearest'
});
}
};
const navigateToSection = (direction) => {
const availableSections = formSections.filter(section => {
if (section === 'apiConfig') {
return showApiConfigCard;
}
return true;
});
let newIndex;
if (direction === 'up') {
newIndex = currentSectionIndex > 0 ? currentSectionIndex - 1 : availableSections.length - 1;
} else {
newIndex = currentSectionIndex < availableSections.length - 1 ? currentSectionIndex + 1 : 0;
}
setCurrentSectionIndex(newIndex);
scrollToSection(availableSections[newIndex]);
};
// 渠道额外设置状态 // 渠道额外设置状态
const [channelSettings, setChannelSettings] = useState({ const [channelSettings, setChannelSettings] = useState({
force_format: false, force_format: false,
@@ -672,6 +717,8 @@ const EditChannelModal = (props) => {
fetchModelGroups(); fetchModelGroups();
// 重置手动输入模式状态 // 重置手动输入模式状态
setUseManualInput(false); setUseManualInput(false);
// 重置导航状态
setCurrentSectionIndex(0);
} else { } else {
// 统一的模态框关闭重置逻辑 // 统一的模态框关闭重置逻辑
resetModalState(); resetModalState();
@@ -1108,7 +1155,41 @@ const EditChannelModal = (props) => {
visible={props.visible} visible={props.visible}
width={isMobile ? '100%' : 600} width={isMobile ? '100%' : 600}
footer={ footer={
<div className='flex justify-end bg-white'> <div className='flex justify-between items-center bg-white'>
<div className='flex gap-2'>
<Button
size='small'
type='tertiary'
icon={<IconChevronUp />}
onClick={() => navigateToSection('up')}
style={{
borderRadius: '50%',
width: '32px',
height: '32px',
padding: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
title={t('上一个表单块')}
/>
<Button
size='small'
type='tertiary'
icon={<IconChevronDown />}
onClick={() => navigateToSection('down')}
style={{
borderRadius: '50%',
width: '32px',
height: '32px',
padding: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'center'
}}
title={t('下一个表单块')}
/>
</div>
<Space> <Space>
<Button <Button
theme='solid' theme='solid'
@@ -1139,7 +1220,11 @@ const EditChannelModal = (props) => {
> >
{() => ( {() => (
<Spin spinning={loading}> <Spin spinning={loading}>
<div className='p-2'> <div
className='p-2'
ref={formContainerRef}
>
<div ref={el => formSectionRefs.current.basicInfo = el}>
<Card className='!rounded-2xl shadow-sm border-0 mb-6'> <Card className='!rounded-2xl shadow-sm border-0 mb-6'>
{/* Header: Basic Info */} {/* Header: Basic Info */}
<div className='flex items-center mb-2'> <div className='flex items-center mb-2'>
@@ -1597,9 +1682,11 @@ const EditChannelModal = (props) => {
/> />
)} )}
</Card> </Card>
</div>
{/* API Configuration Card */} {/* API Configuration Card */}
{showApiConfigCard && ( {showApiConfigCard && (
<div ref={el => formSectionRefs.current.apiConfig = el}>
<Card className='!rounded-2xl shadow-sm border-0 mb-6'> <Card className='!rounded-2xl shadow-sm border-0 mb-6'>
{/* Header: API Config */} {/* Header: API Config */}
<div className='flex items-center mb-2'> <div className='flex items-center mb-2'>
@@ -1790,9 +1877,11 @@ const EditChannelModal = (props) => {
</div> </div>
)} )}
</Card> </Card>
</div>
)} )}
{/* Model Configuration Card */} {/* Model Configuration Card */}
<div ref={el => formSectionRefs.current.modelConfig = el}>
<Card className='!rounded-2xl shadow-sm border-0 mb-6'> <Card className='!rounded-2xl shadow-sm border-0 mb-6'>
{/* Header: Model Config */} {/* Header: Model Config */}
<div className='flex items-center mb-2'> <div className='flex items-center mb-2'>
@@ -1989,8 +2078,10 @@ const EditChannelModal = (props) => {
extraText={t('键为请求中的模型名称,值为要替换的模型名称')} extraText={t('键为请求中的模型名称,值为要替换的模型名称')}
/> />
</Card> </Card>
</div>
{/* Advanced Settings Card */} {/* Advanced Settings Card */}
<div ref={el => formSectionRefs.current.advancedSettings = el}>
<Card className='!rounded-2xl shadow-sm border-0 mb-6'> <Card className='!rounded-2xl shadow-sm border-0 mb-6'>
{/* Header: Advanced Settings */} {/* Header: Advanced Settings */}
<div className='flex items-center mb-2'> <div className='flex items-center mb-2'>
@@ -2207,8 +2298,10 @@ const EditChannelModal = (props) => {
)} )}
/> />
</Card> </Card>
</div>
{/* Channel Extra Settings Card */} {/* Channel Extra Settings Card */}
<div ref={el => formSectionRefs.current.channelExtraSettings = el}>
<Card className='!rounded-2xl shadow-sm border-0 mb-6'> <Card className='!rounded-2xl shadow-sm border-0 mb-6'>
{/* Header: Channel Extra Settings */} {/* Header: Channel Extra Settings */}
<div className='flex items-center mb-2'> <div className='flex items-center mb-2'>
@@ -2311,6 +2404,7 @@ const EditChannelModal = (props) => {
/> />
</Card> </Card>
</div> </div>
</div>
</Spin> </Spin>
)} )}
</Form> </Form>