作者:全球时_尚热门焦点吧 | 来源:互联网 | 2023-08-10 11:52
前言
如下图,实现一个可以自定义的表单。本文以实现思路为主,并未提供完整代码。
思路
1、页面布局为左中右布局:左边显示可以定义的表单控件,中间显示预览效果,右边则为选中某个控件后可以编辑的属性。如下图,先择单选项后,可以编辑选项。
2、使用 vuedraggable 拖拽组件,直接拖动左边的控件到中间生成表单。vuedraggable 文档可查阅:
https://github.com/SortableJS/Vue.Draggable
(你也可以不使用该组件,直接利用点击事件,点击左边控件后在中间生成表单)
主要实现
以文本和选项为例,其他控件可自行拓展。
html 部分
<template><div><el-row :gutter&#61;"20"><el-col :span&#61;"3"><h4>基础控件h4><draggable :options&#61;"dragOptions" v-model&#61;"compList" &#64;end&#61;"end1" :move&#61;"onMove1"><div v-for&#61;"(item, index) in compList" :key&#61;"index" class&#61;"comp-item pointer"><i :class&#61;"item.icon">i>{{item.label}}div>draggable>el-col><el-col :span&#61;"16" style&#61;"border-left:1px solid #DCDFE6;border-right:1px solid #DCDFE6"><el-row><el-col :span&#61;"12"><h4>表单内容h4>el-col>el-row><template v-if&#61;" 0 &#61;&#61; (formList &&formList.length)"><divstyle&#61;"line-height: 50px;height: 50px;border: 1px dashed #d9d9d9;padding-left:20px;margin-top:10px">拖动左边的控件进行自定义字段编辑div>template><draggablestyle&#61;"height:calc(100vh - 220px);overflow-y:auto"group&#61;"comp"v-model&#61;"formList"&#64;start&#61;"start2"&#64;end&#61;"end2":move&#61;"onMove2"><divclass&#61;"temp-content"&#64;click&#61;"activeIndex&#61;index":class&#61;"activeIndex&#61;&#61;index?&#39;temp-active&#39;:&#39;&#39;"v-for&#61;"(item, index) in formList":key&#61;"index"><div style&#61;"padding:10px"><el-row><el-col :span&#61;"20"><inputplaceholder&#61;"请输入字段标题&#xff0c;8个字内"maxlength&#61;"8"v-model&#61;"item.label"style&#61;"border:none;width:100%;outline: none;"/>el-col><el-col :span&#61;"3" class&#61;"text-right pointer"><template v-if&#61;"activeIndex&#61;&#61;index"><i class&#61;"el-icon-delete text-red" title&#61;"删除" &#64;click.stop&#61;"delItem(index)">i>template>el-col>el-row>div><div><template v-if&#61;"item.type&#61;&#61;&#39;radio&#39;"><el-radio-group disabled><el-radio v-for&#61;"(it, idx) in item.options" :key&#61;"idx" :label&#61;"idx">{{it}}el-radio>el-radio-group>template><template v-else-if&#61;"item.type&#61;&#61;&#39;checkbox&#39;"><el-checkbox-group disabled><el-checkbox v-for&#61;"(it, idx) in item.options" :key&#61;"idx" :label&#61;"idx">{{it}}el-checkbox>el-checkbox-group>template><template v-else><el-input disabled :placeholder&#61;"item.placeholder?item.placeholder:&#39;待填写者输入&#39;">el-input>template>div>div>draggable>el-col><el-col :span&#61;"5"><template v-if&#61;"formList&&formList.length>0"><h4><i :class&#61;"formList[activeIndex].icon">i>{{formList[activeIndex].typeName}}h4><el-form style&#61;"margin-top:10px" label-suffix&#61;"&#xff1a;"><el-form-item label&#61;"标题"><el-input v-model&#61;"formList[activeIndex].label">el-input>el-form-item><templatev-if&#61;"formList[activeIndex].type !&#61; &#39;radio&#39; && formList[activeIndex].type !&#61; &#39;checkbox&#39; && formList[activeIndex].type !&#61; &#39;file&#39; && formList[activeIndex].type !&#61; &#39;rate&#39; "><el-form-item label&#61;"提示文字"><el-input v-model&#61;"formList[activeIndex].placeholder">el-input>el-form-item>template><templatev-if&#61;"formList[activeIndex].type &#61;&#61; &#39;radio&#39; || formList[activeIndex].type &#61;&#61; &#39;checkbox&#39;"><el-form-item label&#61;"选项"><el-rowstyle&#61;"margin-top:10px"v-for&#61;"(item, index) in formList[activeIndex].options":key&#61;"index"><el-col :span&#61;"22"><el-input v-model&#61;"formList[activeIndex].options[index]">el-input>el-col><el-col :span&#61;"2" class&#61;"text-center"><i class&#61;"el-icon-delete text-red" &#64;click&#61;"delOption(index)">i>el-col>el-row><div style&#61;"margin-top:10px"><el-button type&#61;"text" &#64;click&#61;"addOption">添加选项el-button>div>el-form-item>template><el-form-item label&#61;"必填"><el-switch v-model&#61;"formList[activeIndex].required">el-switch>el-form-item>el-form>template>el-col>el-row>div>
template>
script 部分
<script>
export default {name: &#39;formBuilder&#39;,props: {templateHtml: {type: String,default: &#39;&#39; }},data() {return {activeIndex: 0, futureIndex: 0, isRight: false, formList: [],dragOptions: { group: { name: &#39;comp&#39;, pull: &#39;clone&#39; }, sort: true },compList: [{label: &#39;单选项&#39;,icon: &#39;el-icon-s-tools&#39;,type: &#39;radio&#39;},{label: &#39;多选项&#39;,icon: &#39;el-icon-s-tools&#39;,type: &#39;checkbox&#39;},{label: &#39;文本&#39;,icon: &#39;el-icon-document-remove&#39;,type: &#39;text&#39;}]}},mounted() {this.renderForm(this.templateHtml)},watch: {templateHtml(newVal) {this.renderForm(newVal)}},methods: {renderForm(newVal) {if (newVal &#61;&#61; &#39;[]&#39; || newVal &#61;&#61; &#39;&#39; || newVal &#61;&#61; null) {this.formList &#61; []} else {this.formList &#61; JSON.parse(newVal)}},getFields() {if (0 &#61;&#61; this.formList.length) {this.$message.error(&#39;必须增加模板字段&#xff01;&#39;)return false}let pass &#61; truelet formList &#61; this.formListfor (let i &#61; 0; i < formList.length; i&#43;&#43;) {formList[i]._vModel &#61; &#39;field&#39; &#43; iif (!formList[i].label) {this.$message.error(&#39;请输入字段标题&#xff01;&#39;)pass &#61; falsebreak}}if (pass) {return this.formList} else {return false}},addOption() {let idx &#61; this.formList[this.activeIndex].options.length &#43; 1this.formList[this.activeIndex].options.push(&#39;选项&#39; &#43; idx)},delOption(index) {this.formList[this.activeIndex].options.splice(index, 1)},delItem(index) {this.formList.splice(index, 1)let leg &#61; this.formList.lengthif (leg &#61;&#61; index && leg > 0) {this.activeIndex &#61; index - 1}},getFormByType(data) {let item &#61; {label: data.label,required: false,type: data.type,icon: data.icon,typeName: data.label,placeholder: &#39;&#39;}if (data.type &#61;&#61; &#39;radio&#39; || data.type &#61;&#61; &#39;checkbox&#39;) {item.options &#61; [&#39;选项1&#39;, &#39;选项2&#39;, &#39;选项3&#39;]}return item},end1(e) {if (this.isRight) {let data &#61; this.getFormByType(this.compList[e.oldIndex])this.formList.splice(this.futureIndex, 0, data)this.activeIndex &#61; this.futureIndex}},start2(e) {},end2(e) {if (e.oldIndex !&#61; e.newIndex && this.activeIndex &#61;&#61; e.oldIndex) {this.activeIndex &#61; e.newIndex}},onMove1(e, originalEvent) {this.futureIndex &#61; e.draggedContext.futureIndexif (e.relatedContext.component.$attrs.group &#61;&#61; &#39;comp&#39;) {this.isRight &#61; true} else {this.isRight &#61; false}return false},onMove2(e, originalEvent) {if (e.relatedContext.component.$attrs.group &#61;&#61; &#39;comp&#39;) {return true} else {return false}}}
}
</script>