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

Vue开发带动态步骤及动态输入字段的表单技术问询

嘿,我完全get到你的需求了——要做一个能动态加步骤的表单,每个步骤里不仅有标题、描述输入,还能点按钮加/删视频输入框,最后要把所有内容转成对象数组对吧?我给你分享一个React的实现方案,思路清晰还容易扩展:

核心实现思路
  • 用一个数组状态来管理所有步骤,每个步骤是一个对象,包含自身的title(标题)、description(描述),还有videos数组(用来存多个视频输入的内容)
  • 每个步骤的视频添加/移除逻辑,通过回调函数同步更新父组件的状态,保证数据和UI一致
  • 提交表单时直接把状态数组转换成你需要的格式,还能顺便做数据清洗(比如过滤空输入)
完整代码示例
import { useState } from 'react';

function DynamicStepForm() {
  // 初始化第一个空步骤
  const [steps, setSteps] = useState([{ title: '', description: '', videos: [] }]);

  // 添加新步骤
  const addStep = () => {
    setSteps([...steps, { title: '', description: '', videos: [] }]);
  };

  // 删除步骤(确保至少保留一个步骤)
  const removeStep = (index) => {
    if (steps.length > 1) {
      const updatedSteps = [...steps];
      updatedSteps.splice(index, 1);
      setSteps(updatedSteps);
    }
  };

  // 更新步骤里的标题/描述字段
  const updateStepField = (stepIndex, fieldName, value) => {
    const updatedSteps = [...steps];
    updatedSteps[stepIndex][fieldName] = value;
    setSteps(updatedSteps);
  };

  // 给指定步骤添加视频输入框
  const addVideoToStep = (stepIndex) => {
    const updatedSteps = [...steps];
    updatedSteps[stepIndex].videos.push(''); // 初始化空输入值
    setSteps(updatedSteps);
  };

  // 移除指定步骤里的某个视频输入
  const removeVideoFromStep = (stepIndex, videoIndex) => {
    const updatedSteps = [...steps];
    updatedSteps[stepIndex].videos.splice(videoIndex, 1);
    setSteps(updatedSteps);
  };

  // 提交表单,处理最终数据
  const handleSubmit = (e) => {
    e.preventDefault();
    // 数据清洗:过滤空的视频输入,去除首尾空格
    const finalData = steps.map(step => ({
      title: step.title.trim(),
      description: step.description.trim(),
      videos: step.videos.filter(video => video.trim() !== '')
    }));
    console.log('最终生成的对象数组:', finalData);
    // 这里可以把finalData发送到后端或者做其他业务处理
  };

  return (
    <form onSubmit={handleSubmit}>
      {steps.map((step, stepIndex) => (
        <div key={stepIndex} style={{ border: '1px solid #eee', padding: '1.5rem', margin: '1rem 0', borderRadius: '8px' }}>
          <h3>步骤 {stepIndex + 1}</h3>
          <div style={{ marginBottom: '1rem' }}>
            <label style={{ display: 'block', marginBottom: '0.5rem' }}>标题:</label>
            <input
              type="text"
              value={step.title}
              onChange={(e) => updateStepField(stepIndex, 'title', e.target.value)}
              placeholder="输入步骤标题"
              style={{ width: '100%', padding: '0.5rem', borderRadius: '4px', border: '1px solid #ddd' }}
            />
          </div>
          <div style={{ marginBottom: '1rem' }}>
            <label style={{ display: 'block', marginBottom: '0.5rem' }}>描述:</label>
            <textarea
              value={step.description}
              onChange={(e) => updateStepField(stepIndex, 'description', e.target.value)}
              placeholder="输入步骤描述"
              rows="3"
              style={{ width: '100%', padding: '0.5rem', borderRadius: '4px', border: '1px solid #ddd' }}
            />
          </div>
          <div style={{ marginBottom: '1rem' }}>
            <button
              type="button"
              onClick={() => addVideoToStep(stepIndex)}
              style={{ padding: '0.5rem 1rem', border: 'none', borderRadius: '4px', backgroundColor: '#28a745', color: 'white', cursor: 'pointer' }}
            >
              添加视频
            </button>
            {step.videos.length > 0 && (
              <div style={{ marginTop: '0.8rem' }}>
                {step.videos.map((video, videoIndex) => (
                  <div key={videoIndex} style={{ marginBottom: '0.5rem', display: 'flex', gap: '0.5rem' }}>
                    <input
                      type="text"
                      value={video}
                      onChange={(e) => {
                        const updatedSteps = [...steps];
                        updatedSteps[stepIndex].videos[videoIndex] = e.target.value;
                        setSteps(updatedSteps);
                      }}
                      placeholder="输入视频链接/ID"
                      style={{ flex: 1, padding: '0.5rem', borderRadius: '4px', border: '1px solid #ddd' }}
                    />
                    <button
                      type="button"
                      onClick={() => removeVideoFromStep(stepIndex, videoIndex)}
                      style={{ padding: '0.5rem 1rem', border: 'none', borderRadius: '4px', backgroundColor: '#dc3545', color: 'white', cursor: 'pointer' }}
                    >
                      移除视频
                    </button>
                  </div>
                ))}
              </div>
            )}
          </div>
          {steps.length > 1 && (
            <button
              type="button"
              onClick={() => removeStep(stepIndex)}
              style={{ padding: '0.5rem 1rem', border: 'none', borderRadius: '4px', backgroundColor: '#dc3545', color: 'white', cursor: 'pointer' }}
            >
              删除此步骤
            </button>
          )}
        </div>
      ))}
      <div style={{ marginBottom: '1rem' }}>
        <button
          type="button"
          onClick={addStep}
          style={{ padding: '0.5rem 1rem', border: 'none', borderRadius: '4px', backgroundColor: '#007bff', color: 'white', cursor: 'pointer', marginRight: '1rem' }}
        >
          添加新步骤
        </button>
        <button
          type="submit"
          style={{ padding: '0.5rem 1rem', border: 'none', borderRadius: '4px', backgroundColor: '#17a2b8', color: 'white', cursor: 'pointer' }}
        >
          提交表单
        </button>
      </div>
    </form>
  );
}

export default DynamicStepForm;
关键细节说明
  1. 状态管理:所有动态内容都通过steps数组状态维护,每次修改都创建新数组(避免直接修改原状态),保证React能正确触发重渲染
  2. 数据清洗:提交时过滤掉空的视频输入,给标题和描述去除首尾空格,确保最终生成的对象数组数据干净
  3. 用户体验:限制至少保留一个步骤,避免表单为空;每个输入框都有占位提示,按钮样式区分功能(添加是绿色/蓝色,删除是红色)

如果你用的是Vue或者其他框架,思路也是类似的——用响应式数组管理步骤,通过方法修改数组内容,最后导出数组即可。

内容的提问来源于stack exchange,提问作者Žilvinas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 10:04:15