vue动态表单DynamicForm
效果:
预期:像这样的表单结构,如果在form中一行一行写每个文本域,有点麻烦,封装成一个组件,同类型支持新增和删除
①DynamicForm.vue
<template> <div class="dynamic-form"> <div class="title"> <p>{{template.title}}</p> <van-icon name="add-o" @click="handleAdd" /> </div> <div class="content" v-for="(item,index) in MyList" :key="index"> <p class="icon" @click="handleDelete(index)"> <img src="@/assets/public/delete.svg"> </p> <van-field v-for="(ele,i) in template.labels" :key="i" v-model="item[ele.field]" rows="3" autosize :label="ele.name" placeholder="请输入" type="textarea" maxlength="200" show-word-limit clearable required :rules="[{required:true,message:'必填项,不能为空'}]" /> </div> </div> </template> <script> export default { model: { prop: 'data', event: 'change' }, computed: { MyList: { get: vm => vm.data, set(data) { this.$emit('change', data) } } }, props: { template: { type: Object, required: true }, data: { type: Array, require: true } }, methods: { handleAdd() { this.MyList.push({}) }, handleDelete(index) { if (this.MyList.length === 1) return this.$toast('必须保留一项') this.MyList.splice(index, 1) } } } </script>
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<style lang="less" scoped> .dynamic-form { & + & { margin-top: 20px; } .title { display: flex; justify-content: space-between; align-items: center; position: relative; > p { margin-left: 15px; font-size: 16px; color: #4d5c82; } .van-icon { color: #198cff; font-size: 20px; font-weight: 600; } .van-icon::after { content: ''; position: absolute; top: -5px; bottom: -5px; left: -5px; right: -5px; border-radius: 50%; } &::before { content: '*'; position: absolute; color: #fc5e5e; } } /deep/ .content { margin-top: 10px; background-color: #f0f4f8; border-radius: 5px; padding: 15px; position: relative; > .icon { position: absolute; right: 15px; top: 10px; z-index: 10; } > .icon::after { content: ''; position: absolute; top: -5px; bottom: -5px; left: -5px; right: -5px; border-radius: 50%; } .van-cell--required::before { color: #fc5e5e; left: 0; } .van-cell::after { border-bottom: none; } .van-cell { padding: 0; line-height: 1; align-items: center; margin-top: 20px; display: block; background-color: #f0f4f8; &:first-of-type { margin-top: 0; } .van-field__label { margin-left: 15px; width: 90%; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; word-break: break-all; > span { font-size: 16px; color: #4d5c82; } } .van-field__value { margin-top: 10px; padding-left: 8px; padding-right: 8px; line-height: 32px; background-color: #fff; border-radius: 5px; input::-webkit-input-placeholder, textarea::-webkit-input-placeholder { color: rgba(#4d5c82, 0.6); } .van-field__control { color: #4d5c82; font-size: 16px; } .van-field__word-limit { position: absolute; right: 5px; bottom: 5px; color: rgba(#4d5c82, 0.6); font-size: 12px; } } } } } </style>
②拜访计划中使用
// 引入 import DynamicForm from '@/components/DynamicForm' // data page2Data: [ { title: '拜访计划', variable: 'requirements', labels: [ { name: '初步预测需求点', field: 'requirement' }, { name: '判断理由', field: 'reason' } ], data: [{}] }, { title: '提问设计(我方提问)', variable: 'questions', labels: [{ name: '问题', field: 'question' }], data: [{}] }, { title: '杜宾清单(客户会提的问题或者顾虑)', variable: 'duBins', labels: [ { name: '问题', field: 'question' }, { name: '解决方案', field: 'solution' } ], data: [{}] }, { title: '信任等级', variable: 'trustLevels', labels: [ { name: '提升计划', field: 'improvementPlan' }, { name: '原因', field: 'reason' } ], data: [{}] }, { title: '物料准备', variable: 'materials', labels: [ { name: '具体物料', field: 'material' }, { name: '选择原因', field: 'reason' } ], data: [{}] } ]
DOM:
<van-form @submit="handleSubmit" validate-trigger='onSubmit'> <DynamicForm v-for="(item,index) in page2Data" :key="index" v-model="item.data" :template='item' /> <div class="page2-footer"> <van-button native-type="submit" :loading='submitLoading'>提交</van-button> </div> </van-form>
得到的数据结构:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
const arr = [ { title: '拜访计划', variable: 'requirements', labels: [ { name: '初步预测需求点', field: 'requirement' }, { name: '判断理由', field: 'reason' } ], data: [{ requirement: '2', reason: '2' }] }, { title: '提问设计(我方提问)', variable: 'questions', labels: [{ name: '问题', field: 'question' }], data: [{ question: '1' }] }, { title: '杜宾清单(客户会提的问题或者顾虑)', variable: 'duBins', labels: [ { name: '问题', field: 'question' }, { name: '解决方案', field: 'solution' } ], data: [{ question: '1', solution: '1' }] }, { title: '信任等级', variable: 'trustLevels', labels: [ { name: '提升计划', field: 'improvementPlan' }, { name: '原因', field: 'reason' } ], data: [{ improvementPlan: '6', reason: '6' }] }, { title: '物料准备', variable: 'materials', labels: [ { name: '具体物料', field: 'material' }, { name: '选择原因', field: 'reason' } ], data: [{ material: '16', reason: '6' }] } ]
③拜访反馈中使用
// 引入 import DynamicForm from '@/components/DynamicForm' // data page2Data: [ { title: '需求调研结果汇总', variable: 'requirementList', labels: [ { name: '客户实际需求', field: 'requirement' }, { name: '判断理由', field: 'reason' } ], data: [{}] }, { title: '客户实际提出的顾虑/问题', variable: 'questionList', labels: [{ name: '顾虑/问题', field: 'concern' }], data: [{}] } ]
DOM:
<van-form @submit="handleSubmit" validate-trigger='onSubmit'> <DynamicForm v-for="(item,index) in page2Data" :key="index" v-model="item.data" :template='item' /> <div class="page2-footer"> <van-button native-type="submit" :loading='submitLoading'>提交</van-button> </div> </van-form>
得到的数据结构:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
const arr = [ { title: '需求调研结果汇总', variable: 'requirementList', labels: [ { name: '客户实际需求', field: 'requirement' }, { name: '判断理由', field: 'reason' } ], data: [ { requirement: '1', reason: '2' }, { requirement: '11', reason: '22' } ] }, { title: '客户实际提出的顾虑/问题', variable: 'questionList', labels: [{ name: '顾虑/问题', field: 'concern' }], data: [{ concern: '1' }] } ]