记录一次手搓自定义表单工具的经历

最近因为疫情需要,每天都要收集公司人员的防疫信息,因此需要设计一款不懂技术的人员使用的自定义表单的系统,我作为前端人员,本来想走捷径使用form-create工具的,想象很美好,当文档第一版完成的时候,需求给我说看不懂,我当时就懵了????居然看不懂??这也是我作为实习生的缺点吧,没有将使用人员搞清楚,对于我来说form-create都比较难看懂,更不要说不懂技术的人了。所以我开始了手搓系统的历程。。。

所用到的技术:vue 、element-ui、vant

第一步,规划页面布局

我参照form-create的风格将页面设置位三块:左侧组件菜单、中间编辑、右侧设置项。

附上一部分代码,仅h5页面展示(如上图所示)

<div class="btns">
            <el-input placeholder="请输入模板名称" v-model="name" class="name"></el-input>
            <el-button @click="saves" type="primary" >保存</el-button>
            <el-button @click="preview" type="primary" >预览</el-button>
            <el-button @click="setNothing" type="primary" >清空</el-button>
        </div>
        <div class="forms">
            <div class="menu">
                <div class="menu-item">
                    <div><span class="title">常用控件<span style="font-size: 12px">(点击添加控件)</span></span></div>
                    <div class="list" >
                        <div class="item" @click="addItem(item)" v-for="item in origin">
                            <img :src="item.icon">
                            <span>{{item.label}}</span>
                        </div>
                    </div>
                </div>
                <div class="menu-item">
                    <div><span class="title">基本控件<span style="font-size: 12px">(点击添加控件)</span></span></div>
                    <div class="list" >
                        <div class="item" @click="addForm(item)" v-for="item in list">
                            <img :src="item.icon">
                            <span>{{item.label}}</span>
                        </div>
                    </div>
                </div>
            </div>
            <div class="content" ref="content">
                <el-form ref="form" class="form" v-for="(item,index) in models" label-width="10%">
                    <!--输入框-->
                    <div class="row" v-if="item.type=='input'">
                        <div  class="pre" :class="{next:isClass==item.labelName}" @click="setRules(item)" :ref="item.labelName">
                            <el-form-item :label="item.label"   :required="item.required=='1'">
                                <el-input style="width: 80%;" :type="item.alt" v-model="form[item.labelName]"></el-input>
                                <el-button type="danger" style="position: absolute;top: 10px;right: 10px;" size="small" @click="deleteItem(item)">删除</el-button>
                            </el-form-item>
                        </div>
                        <div class="img" v-show="isClass==item.labelName">
                            <img src="../../assets/icon/up.png" @click="up(index)">
                            <img src="../../assets/icon/down.png" @click="down(index)">
                        </div>
                    </div>

                    <!--单选框-->
                    <div class="row" v-if="item.type=='radio'"  >
                        <div class="pre" :class="{next:isClass==item.labelName}" @click="setRules(item)" :ref="item.labelName">
                            <el-form-item :label="item.label"  :required="item.required=='1'">
                                <el-radio-group v-model="form[item.labelName]" v-for="k in item.opt">
                                    <el-radio :label="k.label" :value="k.value" style="margin-right: 20px">{{k.label}}</el-radio>
                                </el-radio-group>
                                <el-button type="danger" style="position: absolute;top: 10px;right: 10px;" size="small" @click="deleteItem(item)">删除</el-button>
                            </el-form-item>
                        </div>
                        <div class="img" v-show="isClass==item.labelName">
                            <img src="../../assets/icon/up.png" @click="up(index)">
                            <img src="../../assets/icon/down.png" @click="down(index)">
                        </div>
                    </div>
                    <!--多选框-->
                    <div class="row" v-if="item.type=='checkbox'">
                    <div class="pre" :class="{next:isClass==item.labelName}" @click="setRules(item)" :ref="item.labelName">
                        <el-form-item :label="item.label" v-if="item.type=='checkbox'">
                            <el-checkbox-group v-model="checkList" @change="(val)=>chenge(val,item.labelName)">
                                <el-checkbox  v-for="(item, index) in item.opt" :key="index" :label="item.label">{{item.label}}</el-checkbox>
                            </el-checkbox-group>
                            <el-button type="danger" style="position: absolute;top: 10px;right: 10px;" size="small" @click="deleteItem(item)">删除</el-button>
                        </el-form-item>
                    </div>
                        <div class="img" v-show="isClass==item.labelName">
                            <img src="../../assets/icon/up.png" @click="up(index)">
                            <img src="../../assets/icon/down.png" @click="down(index)">
                        </div>
                    </div>
                    <!--标题-->
                    <div class="row" v-if="item.type=='h2'">
                    <div class="pre" :class="{next:isClass==item.labelName}" @click="setRules(item)" :ref="item.labelName">
                        <el-form-item>
                            <span style="font-size: 18px;font-weight: bolder">{{item.label}}</span>
                            <el-button type="danger" style="position: absolute;top: 10px;right: 10px;" size="small" @click="deleteItem(item)">删除</el-button>
                        </el-form-item>
                    </div>
                        <div class="img" v-show="isClass==item.labelName">
                            <img src="../../assets/icon/up.png" @click="up(index)">
                            <img src="../../assets/icon/down.png" @click="down(index)">
                        </div>
                    </div>
                    <!--时间选择器-->
                    <div class="row" v-if="item.type=='time'">
                    <div class="pre" :class="{next:isClass==item.labelName}" @click="setRules(item)" :ref="item.labelName">
                        <el-form-item :label="item.label"  :required="item.required=='1'">
                            <el-time-picker v-model="form[item.labelName]"></el-time-picker>
                            <el-button type="danger" style="position: absolute;top: 10px;right: 10px;" size="small" @click="deleteItem(item)">删除</el-button>
                        </el-form-item>
                    </div>
                        <div class="img" v-show="isClass==item.labelName">
                            <img src="../../assets/icon/up.png" @click="up(index)">
                            <img src="../../assets/icon/down.png" @click="down(index)">
                        </div>
                    </div>

                    <!--时间段选择器-->
                    <div class="row" v-if="item.type=='times'">
                        <div class="pre" :class="{next:isClass==item.labelName}" @click="setRules(item)" :ref="item.labelName">
                            <el-form-item :label="item.label"  :required="item.required=='1'">
                                <el-time-picker
                                        is-range
                                        v-model="form[item.labelName]"
                                        range-separator="至"
                                        start-placeholder="开始时间"
                                        end-placeholder="结束时间"
                                        placeholder="选择时间范围">
                                </el-time-picker>
                                <el-button type="danger" style="position: absolute;top: 10px;right: 10px;" size="small" @click="deleteItem(item)">删除</el-button>
                            </el-form-item>
                        </div>
                        <div class="img" v-show="isClass==item.labelName">
                            <img src="../../assets/icon/up.png" @click="up(index)">
                            <img src="../../assets/icon/down.png" @click="down(index)">
                        </div>
                    </div>

                    <!--日期选择器-->
                    <div class="row" v-if="item.type=='date'">
                    <div class="pre" :class="{next:isClass==item.labelName}" @click="setRules(item)" :ref="item.labelName">
                        <el-form-item :label="item.label"  :required="item.required=='1'">
                            <el-date-picker
                                    v-model="form[item.labelName]"
                                    type="date">
                            </el-date-picker>
                            <el-button type="danger" style="position: absolute;top: 10px;right: 10px;" size="small" @click="deleteItem(item)">删除</el-button>
                        </el-form-item>
                    </div>
                        <div class="img" v-show="isClass==item.labelName">
                            <img src="../../assets/icon/up.png" @click="up(index)">
                            <img src="../../assets/icon/down.png" @click="down(index)">
                        </div>
                    </div>

                    <!--日期区间选择器-->
                    <div class="row" v-if="item.type=='dates'">
                        <div class="pre" :class="{next:isClass==item.labelName}" @click="setRules(item)" :ref="item.labelName">
                            <el-form-item :label="item.label"  :required="item.required=='1'">
                            <el-date-picker
                                    v-model="form[item.labelName]"
                                    type="daterange"
                                    range-separator="至"
                                    start-placeholder="开始日期"
                                    end-placeholder="结束日期">
                            </el-date-picker>
                            <el-button type="danger" style="position: absolute;top: 10px;right: 10px;" size="small" @click="deleteItem(item)">删除</el-button>
                        </el-form-item>
                        </div>
                        <div class="img" v-show="isClass==item.labelName">
                            <img src="../../assets/icon/up.png" @click="up(index)">
                            <img src="../../assets/icon/down.png" @click="down(index)">
                        </div>
                    </div>


                    <!--上传图片-->
                    <div class="row" v-if="item.type=='upload'" >
                        <div class="pre" :class="{next:isClass==item.labelName}" @click="setRules(item)" :ref="item.labelName">
                            <el-form-item :label="item.label"  :required="item.required=='1'">
                                <el-upload
                                        class="avatar-uploader"
                                        action="http://10.206.1.107:10007/mango/app/upload"
                                        :show-file-list="false"
                                        :on-success="handleAvatarSuccess"
                                        :before-upload="beforeAvatarUpload">
                                    <img v-if="form.imageUrl" :src="form.imageUrl" class="avatar">
                                    <i v-else class="el-icon-plus avatar-uploader-icon"></i>
                                </el-upload>
                                <el-button type="danger" style="position: absolute;top: 10px;right: 10px;" size="small" @click="deleteItem(item)">删除</el-button>
                            </el-form-item>
                        </div>
                        <div class="img" v-show="isClass==item.labelName">
                            <img src="../../assets/icon/up.png" @click="up(index)">
                            <img src="../../assets/icon/down.png" @click="down(index)">
                        </div>
                    </div>

                </el-form>
            </div>
            <div class="config">
                <div class="title">
                    <span class="title">表单配置</span>
                </div>
                <div class="configs" v-show="isShow">

                    <!--是否必填-->
                    <div v-if="rule.type!='h2'">
                        <span  class="h3">是否必填</span>
                        <el-switch v-model="rule.required" @change="(val)=>setRequired(val,item)"></el-switch>
                    </div>

                    <div v-show="!isOrigin" style="display: flex;flex-direction: column;">
                        <!--上传地址-->
                        <!--<div v-if="rule.type=='upload'" v-show="rule.labelName!='healthImg'||rule.labelName!='heImg'">-->
                            <!--<span class="h3">上传地址</span>-->
                            <!--<el-input v-model="rule.opt[action]"></el-input>-->
                        <!--</div>-->

                        <!--字段名称-->
                        <div>
                            <span class="h3">字段名称</span>
                            <el-input v-model="rule.label"></el-input>
                        </div>

                        <!--选项值-->
                        <div v-if="rule.type=='radio'||rule.type=='checkbox'" class="opt">
                            <span class="h3">选项值</span>
                            <div class="btn">
                                <el-button size="small" @click="addOpt(rule.opt)" style="font-size: 16px">+</el-button>
                                <el-button size="small" @click="removeOpt(rule.opt)" style="font-size: 16px">-</el-button>
                            </div>
                            <div v-for="(k,i) in rule.opt">
                                <div style="display: flex;flex-direction: row;justify-content: space-around;align-items: center">
                                    <span>选项{{i+1}}:</span>
                                    <el-input v-model="k.label" @change="setValue(k)"></el-input>
                                </div>
                            </div>
                        </div>



                        <!--校验规则-->
                        <div v-if="rule.type=='input'" v-show="rule.labelName!='name'||rule.labelName!='tel'">
                            <span class="h3">校验规则</span>
                            <el-select v-model="rule.regular" placeholder="请选择">
                                <el-option
                                        v-for="item in options"
                                        :key="item.value"
                                        :label="item.label"
                                        :value="item.pattern">
                                </el-option>
                            </el-select>
                        </div>

                        <!--文本类型-->
                        <div v-if="rule.type=='input'">
                            <span class="h3" >文本类型</span>
                            <el-select v-model="rule.alt" placeholder="请选择">
                                <el-option
                                        v-for="item in alts"
                                        :key="item.value"
                                        :label="item.label"
                                        :value="item.value">
                                </el-option>
                            </el-select>
                        </div>
                    </div>


                </div>
                <span v-show="!isShow" style="margin: 10px;color: #999">请选择一个组件</span>
            </div>
        </div>

 第二步,设置左侧按钮对象

本来数据库就建好了,所以我就根据我需要传输的数据建了对象数组,既可以传输数据,又可以展示页面,我们需要两组对象,一个是常用的,一个是表单所有的类型,分别取名origin和list

在html中使用v-for循环显示按钮,并且添加点击事件(如上述代码)

data(){
return{
origin:[
          {
            type:"input",//表单项类型:input、radio、checkbox等
            icon:name,//我左侧展示的小图标(这个在传入数据库的时候不需要传入)
            label:"姓名",//表单项文字描述
            labelName:'name',//表单项的key
            alt:"text",//input的类型
            opt:[],//单选或者多选的选项列表
            regular:"null",//是否有正则约束,没有就是null
            required:1 //是否必填
          },
          {
            type:"input",
            icon:tel,
            label:"手机号码",
            labelName:'tel',
            alt:"text",
            opt:[],
            regular:"^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\\d{8}$",
            required:1
          },
]
list:[
            {
              type:"input",
              icon:input,
              label:"输入框",
              labelName:null,
              alt:"text",
              regular:"null",
              required:0
            },
            {
            type:"radio",
            icon:radio,
            label:"单选框",
            labelName:'radio',
            alt:"",
            regular:"null",
            required:0,
            opt:[
              {
                label:"选项1",
                value:"选项1"
              },
              {
                label:"选项2",
                value:"选项2"
              }
            ]
            },
]

}}

第三步,设置中间显示对象

中间也是使用v-for显示选中的组件,结构与左侧差不多,少了一个icon属性

//中间已选中组件对象
models:[
            //标题组件
          {
            type:"h2",
            icon:h2,
            label:"基本信息",
            labelName:'base',
            alt:"",
            regular:"null",
            required:0
          },
            //输入框组件
          {
            type:"input",
            icon:input,
            label:"姓名",
            labelName:'name',
            alt:"text",
            opt:[],
            regular:"null",
            required:1
          },
]

第四步,右侧规则设置板块

在中间板块,选中一个组件之后可以在右侧面板中设置这个组件的约束规则,我在data中船舰一个rule对象,在html中循环显示需要设置的属性。根据点击事件传入当前选中对象

<!--输入框-->
<div class="row" v-if="item.type=='input'">
     <div  class="pre" :class="{next:isClass==item.labelName}" @click="setRules(item)" :ref="item.labelName">
      <el-form-item :label="item.label"   :required="item.required=='1'">
      <el-input style="width: 80%;" :type="item.alt" v-model="form[item.labelName]"></el-input>
<!--删除组件的按钮-->
       <el-button type="danger" style="position: absolute;top: 10px;right: 10px;" size="small" @click="deleteItem(item)">删除</el-button>
       </el-form-item>
       </div>
<!--上下移动位置的按钮-->
       <div class="img" v-show="isClass==item.labelName">
       <img src="../../assets/icon/up.png" @click="up(index)">
       <img src="../../assets/icon/down.png" @click="down(index)">
      </div>
</div>
<!--以下是一些基本的规则,可自行添加或者删除,主要还是自己的需求-->
<div class="config">
                <div class="title">
                    <span class="title">表单配置</span>
                </div>
                <div class="configs" v-show="isShow">

                    <!--是否必填-->
                    <div v-if="rule.type!='h2'">
                        <span  class="h3">是否必填</span>
                        <el-switch v-model="rule.required" @change="(val)=>setRequired(val,item)"></el-switch>
                    </div>

                    <div v-show="!isOrigin" style="display: flex;flex-direction: column;">
                        <!--上传地址-->
                        <!--<div v-if="rule.type=='upload'" v-show="rule.labelName!='healthImg'||rule.labelName!='heImg'">-->
                            <!--<span class="h3">上传地址</span>-->
                            <!--<el-input v-model="rule.opt[action]"></el-input>-->
                        <!--</div>-->

                        <!--字段名称-->
                        <div>
                            <span class="h3">字段名称</span>
                            <el-input v-model="rule.label"></el-input>
                        </div>

                        <!--选项值-->
                        <div v-if="rule.type=='radio'||rule.type=='checkbox'" class="opt">
                            <span class="h3">选项值</span>
                            <div class="btn">
                                <el-button size="small" @click="addOpt(rule.opt)" style="font-size: 16px">+</el-button>
                                <el-button size="small" @click="removeOpt(rule.opt)" style="font-size: 16px">-</el-button>
                            </div>
                            <div v-for="(k,i) in rule.opt">
                                <div style="display: flex;flex-direction: row;justify-content: space-around;align-items: center">
                                    <span>选项{{i+1}}:</span>
                                    <el-input v-model="k.label" @change="setValue(k)"></el-input>
                                </div>
                            </div>
                        </div>



                        <!--校验规则-->
                        <div v-if="rule.type=='input'" v-show="rule.labelName!='name'||rule.labelName!='tel'">
                            <span class="h3">校验规则</span>
                            <el-select v-model="rule.regular" placeholder="请选择">
                                <el-option
                                        v-for="item in options"
                                        :key="item.value"
                                        :label="item.label"
                                        :value="item.pattern">
                                </el-option>
                            </el-select>
                        </div>

                        <!--文本类型-->
                        <div v-if="rule.type=='input'">
                            <span class="h3" >文本类型</span>
                            <el-select v-model="rule.alt" placeholder="请选择">
                                <el-option
                                        v-for="item in alts"
                                        :key="item.value"
                                        :label="item.label"
                                        :value="item.value">
                                </el-option>
                            </el-select>
                        </div>
                    </div>


                </div>
                <span v-show="!isShow" style="margin: 10px;color: #999">请选择一个组件</span>
            </div>

好了,今天就到这,下次继续功能实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个简单的使用Vue实现的自定义表单示例: HTML代码: ``` <div id="app"> <form> <custom-input v-model="name" label="Name"></custom-input> <custom-input v-model="email" label="Email" type="email"></custom-input> <custom-input v-model="password" label="Password" type="password"></custom-input> <custom-checkbox v-model="agree" label="I agree to the Terms and Conditions"></custom-checkbox> <button type="submit">Submit</button> </form> </div> ``` Vue组件代码: ``` Vue.component('custom-input', { props: ['value', 'label', 'type'], template: ` <div> <label>{{ label }}</label> <input v-bind:value="value" v-bind:type="type" v-on:input="$emit('input', $event.target.value)"> </div> ` }); Vue.component('custom-checkbox', { props: ['value', 'label'], template: ` <div> <input type="checkbox" v-bind:checked="value" v-on:change="$emit('input', $event.target.checked)"> <label>{{ label }}</label> </div> ` }); new Vue({ el: '#app', data: { name: '', email: '', password: '', agree: false } }); ``` 这个示例中,我们定义了两个自定义组件:custom-input和custom-checkbox。custom-input用于输入文本,支持自定义标签和类型(默认为text)。custom-checkbox用于显示复选框,支持自定义标签。 在Vue实例中,我们定义了四个数据属性:name、email、password和agree。这些属性分别绑定到自定义组件的value或checked属性中。当用户在表单中输入时,这些数据属性会随之改变。 通过这种方法,我们可以轻松地创建自定义表单,并将数据与Vue实例绑定。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值