vue3-steppy基于API动态生成步骤时的步号动态更新问题
Root Cause
vue3-steppy’s default step numbering relies on the initial step array’s index. When you dynamically update steps via API, the component doesn’t recalculate the current step’s position in the new list, leading to mismatched step numbers (like showing #1 when you’re actually on the second dynamic step).
Solutions
1. Custom Step Numbering (Recommended)
Override the step title slot to manually calculate and display the correct step number, instead of relying on the component’s internal index:
<template> <vue3-steppy :steps="dynamicConflictSteps" :active-step="currentStepIdx" @next="goToNextStep" @prev="goToPrevStep" > <!-- Custom step title with dynamic number --> <template #step-title="{ step }"> #{{ getStepPosition(step) }} {{ step.conflictType }} </template> <!-- Step content --> <template #content="{ step }"> <ConflictResolver :conflict="step" /> </template> </vue3-steppy> </template> <script setup> import { ref } from 'vue' import Vue3Steppy from 'vue3-steppy' import 'vue3-steppy/dist/vue3-steppy.css' import ConflictResolver from './components/ConflictResolver.vue' const dynamicConflictSteps = ref([]) const currentStepIdx = ref(0) // Fetch conflict data from API const loadConflictSteps = async () => { const response = await fetch('/api/conflicts') const conflictData = await response.json() // Map API data to step format dynamicConflictSteps.value = conflictData.map(conflict => ({ conflictType: conflict.type, ...conflict })) } // Calculate current step's 1-based number const getStepPosition = (step) => { return dynamicConflictSteps.value.indexOf(step) + 1 } // Navigation handlers const goToNextStep = () => { if (currentStepIdx.value < dynamicConflictSteps.value.length - 1) { currentStepIdx.value++ } } const goToPrevStep = () => { if (currentStepIdx.value > 0) { currentStepIdx.value-- } } // Initialize on mount loadConflictSteps() </script>
2. Force Component Re-render (Quick Fix)
If custom numbering isn’t feasible, trigger a full re-render of vue3-steppy when steps update by changing its key prop. This resets the component’s internal state to match the new step list:
<template> <vue3-steppy :key="stepListKey" :steps="dynamicConflictSteps" :active-step="currentStepIdx" @next="goToNextStep" @prev="goToPrevStep" > <!-- Step content here --> </vue3-steppy> </template> <script setup> import { ref } from 'vue' import Vue3Steppy from 'vue3-steppy' const dynamicConflictSteps = ref([]) const currentStepIdx = ref(0) const stepListKey = ref(0) const loadConflictSteps = async () => { const response = await fetch('/api/conflicts') const conflictData = await response.json() dynamicConflictSteps.value = conflictData.map(conflict => ({ title: conflict.type, content: conflict })) // Increment key to force re-render stepListKey.value++ } </script>
3. Guard Against Index Out-of-Bounds
If steps can be added/removed dynamically, add a watcher to keep the current step index within valid bounds:
import { watch } from 'vue' watch(dynamicConflictSteps, (newSteps) => { if (currentStepIdx.value >= newSteps.length) { currentStepIdx.value = newSteps.length > 0 ? newSteps.length - 1 : 0 } }, { deep: true })
Notes
- Custom numbering is the most robust approach—it gives you full control over step number logic, regardless of component internals.
- The key-based re-render works for simple cases but may reset any unsaved state in step components (like form inputs), so use it cautiously.
- The watcher prevents errors if the step list shrinks while the user is on a step that no longer exists.
内容的提问来源于stack exchange,提问作者Pave

