| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- import React from 'react';
- type StepStatus = 'wait' | 'process' | 'finish';
- /**
- * StepItem 用于描述步骤条的单个步骤信息
- */
- export interface StepItem {
- /** 步骤标题 */
- title: string;
- /** 步骤的说明文本(可选) */
- description?: string;
- /** 步骤序号(可选) */
- number?: number;
- /** 步骤状态,可选: wait | process | finish,默认为 wait */
- status?: StepStatus;
- /** 可自定义扩展内容(可选) */
- extra?: React.ReactNode;
- }
- export interface StepProps {
- steps: StepItem[];
- className?: string;
- showArrow?: boolean;
- onStepClick?: (step: StepItem, index: number) => void;
- }
- const gradientPresets = [
- 'bg-gradient-to-r from-[#95D3F4] to-white',
- 'bg-gradient-to-r from-[#9ABEFF] to-white',
- 'bg-gradient-to-r from-[#87CEFA] to-white',
- ];
- const statusRingMap: Record<StepStatus, string> = {
- wait: 'ring-1 ring-slate-200',
- process: 'ring-2 ring-[#4195E5]',
- finish: 'ring-2 ring-emerald-300',
- };
- const statusTitleMap: Record<StepStatus, string> = {
- wait: 'text-gray-900',
- process: 'text-[#1d5fbf]',
- finish: 'text-emerald-700',
- };
- const cx = (...classNames: Array<string | false | undefined>) => classNames.filter(Boolean).join(' ');
- const Step: React.FC<StepProps> = ({ steps, className, showArrow = true, onStepClick }) => {
- if (!steps?.length) {
- return null;
- }
- return (
- <div className={cx('flex items-center w-full', className)}>
- {steps.map((step, index) => {
- const status = step.status ?? 'wait';
- const gradient = gradientPresets[index % gradientPresets.length];
- const card = (
- <div
- key={step.title ?? index}
- className={cx(
- 'relative h-[100px] flex-1 rounded-xl p-6 shadow-md transition-all duration-200 flex flex-col justify-center',
- gradient,
- statusRingMap[status],
- onStepClick && 'cursor-pointer hover:-translate-y-1',
- )}
- onClick={() => onStepClick?.(step, index)}
- >
- <div className="absolute top-3 right-4 text-right italic">
- <div className="text-3xl font-bold text-[#4195E5]">{step.number ?? index + 1}</div>
- <div className="text-xs font-medium text-[#4195E5] text-opacity-80 uppercase">step</div>
- </div>
- <p className={cx('mb-2 text-lg font-semibold', statusTitleMap[status])}>{step.title}</p>
- {step.description && (
- <p className="text-sm leading-relaxed text-gray-600 w-[80%]">{step.description}</p>
- )}
- {step.extra && <div className="mt-3">{step.extra}</div>}
- </div>
- );
- return (
- <React.Fragment key={`${step.title}-${index}`}>
- {card}
- {showArrow && index < steps.length - 1 && (
- <div className="mx-3 text-lg font-semibold text-gray-400">{'>'}</div>
- )}
- </React.Fragment>
- );
- })}
- </div>
- );
- };
- Step.displayName = 'Step';
- export default Step;
|