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

Vue3+FormKit实现嵌套列表表单(无需Repeater组件)

在Vue 3中用FormKit实现嵌套动态表单(无Repeater)

以下是实现支持动态添加孩子、每个孩子动态添加玩具的完整代码,解决你当前嵌套层级无法绑定的问题:

修改后的完整代码

<script setup lang="ts">
import Swal from 'sweetalert2';
import { ref, inject, onMounted } from 'vue';
import { token } from '@formkit/utils';

const lisyConfig = inject("config");
const foundationId = inject("foundationId");

const makeToysRequests = async (fields: any) => {
  console.log('提交数据:', fields);
  await new Promise((r) => setTimeout(r, 1000))
  alert(JSON.stringify(fields))
}

const form = ref();
const formRef = ref();

// 初始化孩子数组,每个孩子自带一个玩具项
const childItems = ref([{ token: token(), requestsPerChild: [{ token: token() }] }]);

// 添加孩子
const addChild = () => {
  childItems.value.push({ token: token(), requestsPerChild: [{ token: token() }] })
}

// 移除孩子
const removeChild = (childToken: string) => {
  Swal.fire({
    title: '确定删除吗?',
    icon: 'warning',
    showCancelButton: true,
    confirmButtonText: '确定',
    cancelButtonText: '取消'
  }).then((result) => {
    if (result.isConfirmed) {
      childItems.value = childItems.value.filter(item => item.token !== childToken);
    }
  });
}

// 给指定孩子添加玩具
const addToy = (childToken: string) => {
  const targetChild = childItems.value.find(child => child.token === childToken);
  if (targetChild) {
    targetChild.requestsPerChild.push({ token: token() });
  }
}

// 移除指定孩子的某个玩具
const removeToy = (childToken: string, toyToken: string) => {
  const targetChild = childItems.value.find(child => child.token === childToken);
  if (targetChild) {
    targetChild.requestsPerChild = targetChild.requestsPerChild.filter(toy => toy.token !== toyToken);
  }
}

const toktok = ref(token());
onMounted(() => {
  console.info('Form app mounted');
  console.info('form config: ', lisyConfig);
})
</script>

<template>
  <div class="lisy-form-wrapper">
    <FormKit 
      type="form"
      ref="formRef"
      v-model="form"
      form-class="-form form-grid"
      id="f_form"
      submit-label="提交"
      @submit="makeToysRequests"
      :actions="false"
    >
      <!-- 家长信息组 -->
      <FormKit type="group" name="partner">
        <h2>家长信息</h2>
        <div class="form-grid address-input-group">
          <FormKit 
            type="text"
            name="firstName"
            label="名字"
            validation="required"
            outer-class="form-grid-column-8"
          />
          <FormKit 
            type="text"
            name="infix"
            label="中间名"
            outer-class="form-grid-column-4"
          />
          <FormKit 
            type="text"
            name="lastName"
            label="姓氏"
            validation="required"
            outer-class="form-grid-column-12"
          />
        </div>
      </FormKit>

      <!-- 孩子列表 -->
      <FormKit type="list" name="childrenList">
        <FormKit
          v-for="(child, childIndex) in childItems"
          :key="child.token"
          :name="`childrenList[${childIndex}]`"
          type="group"
        >
          <div class="lisy-form-child">
            <!-- 移除孩子按钮 -->
            <FormKit 
              v-if="childIndex !== 0" 
              type="button" 
              @click="removeChild(child.token)" 
              prefix-icon="trash" 
              input-class="btn outline"
              wrapper-class="btn__wrapper btn__wrapper--right"
            >
              删除孩子
            </FormKit>

            <!-- 孩子基本信息 -->
            <FormKit
              type="radio"
              name="gender"
              validation="required"
              label="性别"
              :options="{
                jongen: '男孩',
                meisje: '女孩',
              }"
              required
            />
            <div class="form-grid">
              <FormKit 
                type="text"
                name="firstName"
                label="名字"
                validation="required"
                outer-class="form-grid-column-5"
              />
              <FormKit 
                type="text"
                name="infix"
                label="中间名"
                outer-class="form-grid-column-2"
              />
              <FormKit 
                type="text"
                name="lastName"
                label="姓氏"
                validation="required"
                outer-class="form-grid-column-5"
              />
            </div>
            <FormKit 
              type="date"
              name="birthdate"
              label="出生日期"
              validation="required"
            />

            <!-- 玩具列表(嵌套层级) -->
            <div class="toy-list">
              <h3>孩子的玩具</h3>
              <FormKit type="list" :name="`requestsPerChild`">
                <FormKit
                  v-for="(toy, toyIndex) in child.requestsPerChild"
                  :key="toy.token"
                  :name="`requestsPerChild[${toyIndex}]`"
                  type="group"
                >
                  <div class="toy-item">
                    <FormKit 
                      v-if="toyIndex !== 0"
                      type="button" 
                      @click="removeToy(child.token, toy.token)" 
                      prefix-icon="trash" 
                      input-class="btn outline small"
                      wrapper-class="btn__wrapper btn__wrapper--right"
                    >
                      删除玩具
                    </FormKit>
                    <FormKit 
                      type="textarea"
                      name="voorziening"
                      label="玩具描述"
                      validation="required"
                    />
                  </div>
                </FormKit>
                <!-- 添加玩具按钮 -->
                <FormKit
                  type="button"
                  @click="addToy(child.token)"
                  prefix-icon="add"
                  input-class="btn purple small"
                  wrapper-class="btn__wrapper btn__wrapper--right"
                >
                  添加玩具
                </FormKit>
              </FormKit>
            </div>
            <hr>
          </div>
        </FormKit>
        <!-- 添加孩子按钮 -->
        <FormKit
          type="button"
          @click="addChild"
          prefix-icon="add"
          input-class="btn purple"
          wrapper-class="btn__wrapper btn__wrapper--right"
        >
          添加孩子
        </FormKit>
      </FormKit>

    </FormKit>
    <pre style="font-size: small">
      孩子数量: {{childItems.length}}
      表单数据: {{form}}
    </pre>
  </div>
</template>

<style lang="scss">
.toy-list {
  margin: 1rem 0;
  padding: 1rem;
  background: #f5f5f5;
  border-radius: 4px;
}
.toy-item {
  margin-bottom: 1rem;
}
</style>

关键修改说明

  1. 嵌套数据绑定修正:

    • 孩子的group组件使用name="childrenList[${childIndex}]",明确其在表单数组中的位置
    • 玩具的group组件使用name="requestsPerChild[${toyIndex}]",让FormKit自动将玩具数据嵌套到对应孩子的requestsPerChild数组中
  2. 玩具的动态操作:

    • 新增addToy方法,通过孩子的token定位到目标孩子,向其requestsPerChild数组添加新玩具项
    • 新增removeToy方法,同理定位孩子和玩具,过滤移除目标玩具
  3. 交互优化:

    • 移除按钮直接传递token参数,避免通过DOM元素的dataset获取,逻辑更清晰
    • 给玩具列表添加简单样式,区分层级

提交表单后,你会得到如下结构的嵌套数据:

{
  "partner": { ... },
  "childrenList": [
    {
      "gender": "jongen",
      "firstName": "...",
      "birthdate": "...",
      "requestsPerChild": [
        { "voorziening": "玩具1描述" },
        { "voorziening": "玩具2描述" }
      ]
    }
  ]
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 09:19:53