最近因为疫情需要,每天都要收集公司人员的防疫信息,因此需要设计一款不懂技术的人员使用的自定义表单的系统,我作为前端人员,本来想走捷径使用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>
好了,今天就到这,下次继续功能实现