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
相关产品推荐
相关产品推荐

