业务定制太多,组件库招架不住?手写一个步骤条!

在实际项目中,起初采用的是 Element Plus 提供的 ElSteps 组件,但随着业务的不断更新,原始组件已经无法满足需要,最终选择手写更贴合业务需求的步骤条组件。

比如,有如下需求的步骤条,第一反应可能是基于现有的 ElSteps 组件进行样式调整,然而在实际尝试过程中发现,如果想实现诸如“将标题在序号后展示”这样的效果,会大量依赖 position 定位等样式,不仅实现复杂,而且后期维护成本很高。因此,放弃对现有组件的改造,转而手写一个。

简单实现,手写一个横向滚动条,垂直滚动条仍沿用 ElSteps。

const Steps = defineComponent({
  name: 'RBSteps',
  props: {
    ...ElSteps.props,
    // 步骤数据
    steps: {
      type: Array,
      default: () => [],
    },
    // 当前激活步骤
    active: {
      type: Number,
      default: 0,
    },
    // 方向
    direction: {
      type: String,
      default: StepBarType.horizontal,
      validator: (value) => [StepBarType.horizontal, StepBarType.vertical].includes(value),
    }
  },
  setup(props, { attrs, slots, expose }) {
    const stepsRef = ref<InstanceType<typeof ElSteps>>();

    expose({
      getStepsInstance: () => stepsRef.value,
    });

    return () => (
      <div class="business-steps-common">
        {props.direction === StepBarType.horizontal ? (
          <div class='steps-horizontal'>
            {props.steps?.map((step, index) => {
              step.status = step.status || StepBarStatus.default;
              return (
                <div class='option'>
                  <div class='step'>
                    {/* 步骤序号圆圈 */}
                    <div class={`circle ${step.status}`}>
                      {[StepBarStatus.default, StepBarStatus.process].includes(step.status) ? (index + 1) : ''}
                    </div>
                    {/* 标题和描述 */}
                    <div class='content' style='padding: 0'>
                      <div class={`title ${step.status}`}>{step.title}</div>
                      <div class={`desc ${step.status}`}>{step.desc}</div>
                    </div>
                  </div>
                  {/* 连接线 */}
                  {index < props.steps.length - 1 && (
                    <div class='line' style={{ width: `calc(100% - 6rem)` }}></div>
                  )}
                </div>
              )
            })}
          </div>
        ) : (
          <ElSteps
            ref={stepsRef}
            v-slots={slots}
            {...props}
            {...attrs}
          />
        )}
      </div>
    )
  },
})

由此可以实现,不同的状态下的步骤条展示。

好处是什么呢?

手写步骤条的最大好处之一在于状态控制的灵活性。在自定义的组件中,每一步的状态可以完全由外层组件进行控制——比如某一步是否激活、是否已完成、是否需要回退,甚至可以根据异步请求的结果或复杂的业务逻辑动态更新。这种能力让步骤条不仅是一个“展示组件”,更具备了“业务状态驱动”的能力,使得整个流程的掌控更精细,也更符合真实的业务场景。

比如:

export default defineComponent({
  name: "Status",
  setup() {
    const steps_dynamic = ref<StepsType[]>([
      { title: '步骤一', desc: '这里是提示文字,这里是提示文字这里是提示文字' },
      { title: '步骤二', desc: '这里是提示文字,这里是提示文字这里是提示文字' },
      { title: '步骤三', desc: '这里是提示文字,这里是提示文字这里是提示文字' },
      { title: '步骤四', desc: '这里是提示文字,这里是提示文字这里是提示文字' },
    ]);

    const activeStep = ref(0);
    const statusMap = {
      0: 'success',
      1: 'error',
      2: 'process',
      3: 'default',
    };

    const handlePrev = () => {
      if (activeStep.value === 0) {
        MyMessage.warning('当前已经是第一步');
        return;
      }

      activeStep.value--;
      steps_dynamic.value[activeStep.value].status = 'default';
    }

    const handleNext = async () => {
      const currentStep = activeStep.value;
      if (currentStep >= steps_dynamic.value.length) {
        MyMessage.info('到达最后一步');
        return;
      }

      const newStatus = statusMap[currentStep];
      if (newStatus) {
        steps_dynamic.value[currentStep].status = newStatus;
        activeStep.value++;
        if (activeStep.value === steps_dynamic.value.length) {
          MyMessage.warning('当前状态是 default,没有样式变化');
        }
      } else {
        steps_dynamic.value[currentStep].status = 'default';
      }
    };

    return () => (
      <div class={styles.steps_wrapper}>
        <MySteps
          active={activeStep.value}
          steps={steps_dynamic.value}
          direction="horizontal"
        />
        <div class={styles.btn_container}>
          <MyButton type="info" plain size='small' onClick={handlePrev}>上一步</MyButton>
          <MyButton type="info" plain size='small' onClick={handleNext}>下一步</MyButton>
        </div>
      </div>
    );
  }
});

每点击一步,就会执行一步。

展示如下:

需要注意 📢

当前横向步骤条实现很简单,若需扩展功能,可根据实际需求自主实现。

总结

手写一个步骤条组件,也许比我们想象中花的时间要多,但它带来的不仅是一个“符合需求的 UI 组件”,更是一次深刻的技术锤炼和设计思维的进化。

当我们从“拿来即用”走向“定制打造”,我们不只是工程师,更是构建体验的设计者。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值