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

Vuejs动态构建多层嵌套复杂JSON并通过v-for渲染展示的方案咨询

实现方案

核心思路

  • 将单个扩展项抽离为可递归调用的独立组件,利用组件自引用来实现无限层级的渲染
  • 调整扩展项的数据结构,新增children字段存储复杂类型下的子扩展项,天然形成树形嵌套结构
  • 给模态框增加当前父节点ID标识,提交新扩展时自动识别要插入的层级(根层级/某个复杂类型的子层级)

具体代码实现

1. 新建递归子组件 src/components/ExtensionItem.vue

<template>
  <div class="form-inline" style="margin-left: 20px;margin-top: 8px;">
    <span>{{ extension.namespace + ":" + extension.localName }}</span>
    <input 
      v-if="extension.dataType == 'string'" 
      type="text" 
      @input="updateText($event)"
    >
    <button 
      v-if="extension.dataType == 'complex'" 
      @click="$emit('add-complex', extension.ID)"
    >
      Add another
    </button>
    <!-- 递归渲染子扩展项 -->
    <ExtensionItem
      v-for="child in extension.children"
      :key="child.ID"
      :extension="child"
      @add-complex="(id) => $emit('add-complex', id)"
      @update-text="updateChildText"
    />
  </div>
</template>

<script>
export default {
  name: 'ExtensionItem', // 组件名必须填写,用来递归调用自身
  props: {
    extension: {
      type: Object,
      required: true
    }
  },
  methods: {
    updateText(e) {
      this.extension.text = e.target.value || ''
    },
    updateChildText(id, value) {
      const findChild = (list) => {
        for(let item of list) {
          if(item.ID === id) return item
          if(item.children?.length) {
            const res = findChild(item.children)
            if(res) return res
          }
        }
      }
      const child = findChild(this.extension.children)
      if(child) child.text = value
    }
  }
}
</script>

2. 修改原有父组件代码

<template>
  <div class="container-fluid">
    <div class="row">
      <div class="col-md-3">
        <span>Extensions</span>
        <button class="form-control" @click="createExtensions()">
          Add Another
        </button>
      </div>
    </div>

    <!-- 渲染首层扩展项 -->
    <ExtensionItem
      v-for="extension in extensionList"
      :key="extension.ID"
      :extension="extension"
      @add-complex="AddComplextExtension"
    />

    <b-modal
      id="Extension"
      title="Add Another Element"
      size="lg"
      width="100%"
      :visible="showModal"
      @hidden="resetModal"
    >
      <b-form id="AddExtension" @submit.prevent="submitExtension">
        <div class="form-group">
          <label for="message-text" class="col-form-label">Namespace URI:</label>
          <input
            v-model="extension.namespace"
            type="text"
            class="form-control"
            required
          >
        </div>
        <div class="form-group">
          <label for="message-text" class="col-form-label">Local Name:</label>
          <input
            v-model="extension.localName"
            type="text"
            class="form-control"
            required
          >
        </div>
        <div class="form-group">
          <label for="AddExtensionDataType" class="col-form-label">Data Type:</label>
          <b-form-select v-model="extension.dataType" class="form-control">
            <b-form-select-option value="string">
              String
            </b-form-select-option>
            <b-form-select-option value="complex">
              Complex
            </b-form-select-option>
          </b-form-select>
        </div>
      </b-form>
      <template #modal-footer="{ cancel }">
        <b-btn @click="cancel">
          Cancel
        </b-btn>
        <b-btn variant="primary" type="submit" form="AddExtension">
          OK
        </b-btn>
      </template>
    </b-modal>
  </div>
</template>

<script>
import ExtensionItem from './ExtensionItem.vue'
export default {
  components: { ExtensionItem },
  data () {
    return {
      extensionList: [],
      extensionCount: 0,
      extension: {
        namespace: '',
        localName: '',
        dataType: 'string'
      },
      showModal: false,
      currentParentId: null // 记录要插入的父节点ID,null代表插入根列表
    }
  },
  methods: {
    createExtensions () {
      this.currentParentId = null
      this.showModal = true
    },

    submitExtension () {
      this.showModal = false
      const extensionObj = {
        ID: this.extensionCount,
        namespace: this.extension.namespace,
        localName: this.extension.localName,
        dataType: this.extension.dataType,
        children: [] // 新增children字段存储子扩展
      }
      // 区分插入根列表还是父节点的children
      if(this.currentParentId === null) {
        this.extensionList.push(extensionObj)
      } else {
        const findParent = (list) => {
          for(let item of list) {
            if(item.ID === this.currentParentId) return item
            if(item.children?.length) {
              const res = findParent(item.children)
              if(res) return res
            }
          }
        }
        const parent = findParent(this.extensionList)
        if(parent) parent.children.push(extensionObj)
      }
      this.extensionCount++
    },

    AddComplextExtension (extensionID) {
      this.currentParentId = extensionID
      this.showModal = true
    },

    resetModal() {
      this.extension = {
        namespace: '',
        localName: '',
        dataType: 'string'
      }
      this.currentParentId = null
    }
  }
}
</script>

<style scoped>
.form-inline {
  display: flex;
  gap: 8px;
  align-items: center;
}
</style>

效果说明

  • 不管添加多少层级的复杂类型扩展,递归组件都会自动渲染对应层级的内容
  • 最终的extensionList直接用JSON.stringify()转换就能得到需要的嵌套扩展JSON结构,支持任意层级的横向(同级新增多个)、纵向(嵌套多层)扩展
  • 原有首层扩展的逻辑完全保留,不需要修改原有交互习惯

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 15:57:03