You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Chakra UI创建含步骤与子步骤的步进进度条

解决方案:基于页面跳转的嵌套步进器实现

针对你需要的页面跳转联动步进器功能,以下是修改后的实现代码,解决了进度条匹配、子步骤勾选与激活的问题:

import {
  Box,
  Flex,
  Progress,
  Step,
  StepIcon,
  StepIndicator,
  StepNumber,
  StepStatus,
  Stepper,
  Text,
  useSteps,
  useMemo
} from '@chakra-ui/react';
import React from 'react';

interface StepperProps {
  subText?: string;
}

// 基础步骤配置
const baseSteps = [
  {
    label: 'Customer Details',
    active: true,
    completed: false,
    subSteps: [
      { label: 'Business Information', active: true, url: '/', completed: false },
      { label: 'Primary Contact', active: false, url: '/primary-contact', completed: false },
      { label: 'Offer Type', active: false, url: '/tradefull-solutions', completed: false },
    ],
  },
  {
    label: 'Supporting Documents',
    active: false,
    completed: false,
    subSteps: [
      { label: 'Documents Upload', active: false, completed: false },
      { label: 'Additional Assets', active: false, completed: false },
      { label: 'Notes', active: false, completed: false },
    ],
  },
  {
    label: 'Review & Submit',
    active: false,
    completed: false,
  },
];

function Steppers({ subText }: StepperProps) {
  const { activeStep, setActiveStep } = useSteps({
    index: 0,
    count: baseSteps.length,
  });

  // 根据当前页面动态计算步骤及子步骤状态
  const currentSteps = useMemo(() => {
    const stepsCopy = JSON.parse(JSON.stringify(baseSteps));
    
    // 处理Customer Details下的子步骤状态
    const customerSubSteps = stepsCopy[0].subSteps;
    if (!customerSubSteps) return stepsCopy;

    switch(subText) {
      case 'business':
        customerSubSteps.forEach((sub, idx) => {
          sub.active = idx === 0;
          sub.completed = idx < 0;
        });
        break;
      case 'contact':
        customerSubSteps.forEach((sub, idx) => {
          sub.active = idx === 1;
          sub.completed = idx < 1;
        });
        stepsCopy[0].completed = false;
        break;
      case 'tradefull':
        customerSubSteps.forEach((sub, idx) => {
          sub.active = idx === 2;
          sub.completed = idx < 2;
        });
        stepsCopy[0].completed = false;
        break;
      default:
        customerSubSteps.forEach((sub, idx) => {
          sub.active = idx === 0;
          sub.completed = false;
        });
    }

    return stepsCopy;
  }, [subText]);

  // 计算进度条百分比
  const getProgressPercent = () => {
    switch(subText) {
      case 'business': return 20;
      case 'contact': return 40;
      case 'tradefull': return 60;
      case 'documents': return 70;
      case 'assets': return 80;
      case 'notes': return 90;
      case 'review': return 100;
      default: return 20;
    }
  };

  return (
    <Box position="relative" display="flex" gap={4}>
      {/* 自定义垂直进度条 */}
      <Box position="relative" width="4px" bg="gray.200" h="full" borderRadius="full">
        <Progress
          position="absolute"
          top="0"
          left="0"
          height="100%"
          width="4px"
          value={getProgressPercent()}
          colorScheme="blue"
          borderRadius="full"
          orientation="vertical"
        />
      </Box>

      <Stepper mt={4} size="xs" index={activeStep} orientation="vertical" gap="0">
        {currentSteps.map((step, index) => (
          <Step key={index} onClick={() => setActiveStep(index)}>
            <StepIndicator>
              <StepStatus 
                complete={<StepIcon />}
                active={<StepNumber>{index + 1}</StepNumber>}
                incomplete={<StepNumber>{index + 1}</StepNumber>}
              />
            </StepIndicator>

            <Box flexShrink="0" mb={2}>
              <Text 
                fontSize="md" 
                fontWeight="medium" 
                color={step.active ? "BaseClr.Primary" : "gray.600"}
                mt={0} mb={0} pb="3"
              >
                {step.label}
              </Text>

              {step.subSteps &&
                step.subSteps.map((sub, subIdx) => (
                  <Flex gap={2} align="center" mb={2} key={subIdx}>
                    {/* 子步骤状态图标 */}
                    {sub.completed && <StepIcon boxSize={4} color="green.500" />}
                    {!sub.completed && sub.active && <Box boxSize={4} borderRadius="full" bg="BaseClr.Primary" />}
                    {!sub.completed && !sub.active && <Box boxSize={4} borderRadius="full" bg="gray.300" />}
                    
                    <Text 
                      mb={0} 
                      fontSize="sm" 
                      fontWeight={sub.active ? "semibold" : "regular"}
                      color={sub.active ? "BaseClr.Primary" : "gray.600"}
                    >
                      {sub.label}
                    </Text>
                  </Flex>
                ))}
            </Box>
          </Step>
        ))}
      </Stepper>
    </Box>
  );
}

export default Steppers;

关键改动说明

  1. 动态状态管理:使用useMemo根据subText参数实时更新步骤和子步骤的active、completed状态,确保页面跳转时状态同步
  2. 自定义垂直进度条:替换默认的StepSeparator,用Progress组件实现垂直方向的进度条,根据当前页面设置对应的进度百分比
  3. 子步骤视觉反馈:给子步骤添加状态图标(完成显示勾选、激活显示实心圆点、未开始显示灰色圆点),同时高亮激活状态的文本
  4. 父步骤状态联动:子步骤未全部完成时,父步骤保持未完成状态,符合步进逻辑

使用时只需在对应页面传入subText参数即可:

  • Business页面:<Steppers subText="business" />
  • Primary Contact页面:<Steppers subText="contact" />
  • Offer Type页面:<Steppers subText="tradefull" />

内容的提问来源于stack exchange,提问作者Maharajan K

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 16:27:03