vue动态配置小程序表单页面(一)__Vue.js
粉丝福利 : 关注VUE中文社区公众号,回复视频领取粉丝福利
通过后台配置小程序表单页面
设计思路
1.设计页面表单组件样式 抽取动态参数及样式
2.每个表单组件独立文件 方便后期维护
3.小程序端根据参数渲染对应组件(使用的是uniapp)表单验证库是async-validator
icon 使用的是阿里矢量图
https://www.iconfont.cn/
一、左边组件
1.整理组件列表
2.配置点击组件默认数据
<template>
<div class="my-menu">
<div class="menu-title">
<span>表单组件</span>
</div>
<div class="navs">
<div class="navs-group">
<template v-for="g in navs">
<div class="title" :key="g.type">
{{g.title}}
</div>
<div class="navs-components am-cf" :key="g.title">
<div class="special" @click="addcomponent(item)" v-for="item in g.item" :key="item.type">
<i class="iconfont" :class="[item.icon]" style="font-size:12px"></i> {{item.name}}
</div>
</div>
</template>
</div>
</div>
<div class="action">
<el-button type="primary" @click="save">保存表单</el-button>
</div>
</div>
</template>
<script>
export default {
name: '',
data() {
return {
navs: [
{
title: '基础字段',
type: 'jc',
item: [
{
type: 'input',
icon: 'iconinput',
name: '单行文本',
},
{
type: 'textarea',
icon: 'iconduohangwenben',
name: '多行文本',
},
{
type: 'select',
icon: 'iconxialakuang',
name: '下拉选择框',
},
{
type: 'radio',
icon: 'iconiconfontoptionbutton',
name: '单选框',
},
{
type: 'checkbox',
icon: 'iconxuanze',
name: '多选框',
},
{
type: 'date',
icon: 'iconshijian',
name: '日期选择',
},
{
type: 'text',
icon: 'iconA',
name: '文字',
},
],
},
{
title: '高级字段',
type: 'gj',
item: [
{
type: 'fileupload',
icon: 'iconshangchuanwenjian',
name: '文件',
},
{
type: 'imgupload',
icon: 'icontupian1',
name: '图片',
},
{
type: 'dynamictable',
icon: 'iconinput',
name: '自增表格',
},
],
},
{
title: '其它',
type: 'qt',
item: [
{
type: 'guide',
icon: 'iconfengexian',
name: '辅助线',
},
],
},
],
defaultimgurl: '',
defaultimgurl1: '',
defaultserviceurl: '',
defaultnoticeurl: '',
}
},
components: {},
created() {},
methods: {
async save() {
this.$emit('save')
},
addcomponent(val) {
let item = {}
switch (val.type) {
case 'select':
case 'checkbox':
case 'radio':
item = {
name: val.name,
type: val.type,
key: `${val.type}_${new Date().getTime()}`,
params: {
placeholder: '',
hidden: false,
defaultvalue: '',
tip: '',
style: {},
},
options: [
{ label: '选项1', value: '选项1' },
{ label: '选项2', value: '选项2' },
{ label: '选项3', value: '选项3' },
],
rules: [],
}
break
case 'dynamictable':
item = {
name: val.name,
type: val.type,
key: `${val.type}_${new Date().getTime()}`,
params: {
placeholder: '',
hidden: false,
defaultvalue: '',
tip: '',
style: {},
min: 2,
},
options: [
{
label: '标题1',
value: 'string',
required: true,
options: '',
key: `${val.type}_dy_${1}_${new Date().getTime()}`,
placeholder: '',
},
{
label: '标题2',
value: 'string',
required: true,
options: '',
key: `${val.type}_dy_${2}_${new Date().getTime()}`,
placeholder: '',
},
{
label: '标题3',
value: 'string',
required: true,
options: '',
key: `${val.type}_dy_${3}_${new Date().getTime()}`,
placeholder: '',
},
],
rules: [],
}
break
case 'date':
item = {
name: val.name,
type: val.type,
key: `${val.type}_${new Date().getTime()}`,
params: {
placeholder: '',
hidden: false,
defaultvalue: '',
type: 'date',
tip: '',
style: {},
},
rules: [],
}
break
case 'fileupload':
case 'imgupload':
item = {
name: val.name,
type: val.type,
key: `${val.type}_${new Date().getTime()}`,
params: {
placeholder: '',
hidden: false,
defaultvalue: '',
type: 'date',
tip: '',
limit: 1,
style: {},
},
rules: [],
}
break
case 'guide':
item = {
name: '辅助线',
type: 'guide',
key: `${val.type}_${new Date().getTime()}`,
rules: [],
params: {
tip: '',
style: {
background: '#ffffff',
lineStyle: 'solid',
lineHeight: 1,
lineColor: '#000000',
paddingTop: 10,
},
},
}
break
default:
item = {
name: val.name,
type: val.type,
key: `${val.type}_${new Date().getTime()}`,
params: {
placeholder: '',
hidden: false,
defaultvalue: '',
tip: '',
style: {},
},
rules: [],
}
break
}
this.$emit('add', item)
},
},
}
</script>
<style lang="scss" scoped>
.my-menu {
width: 260px;
height: auto;
background: #fdfdfd;
border: 1px solid #ddd;
padding: 15px 10px;
transition: all 0.3s;
user-select: none;
.menu-title {
position: relative;
padding: 0 22px;
height: 30px;
border-bottom: 1px solid #eef1f5;
line-height: 30px;
&:before {
content: '';
position: absolute;
width: 4px;
height: 13px;
background: #00aeff;
top: 8px;
left: 9px;
}
}
.navs {
padding: 10px 5px;
border-bottom: 1px dotted #ddd;
position: relative;
display: flex;
.navs-group {
.title {
// font-size: 1.24rem;
color: #999;
margin: 10px 0;
}
.navs-components {
.special {
width: 100px;
height: 26px;
float: left;
line-height: 26px;
margin: 5px;
border: 1px solid #dddddd;
text-align: left;
text-indent: 10px;
font-size: 12px;
cursor: pointer;
transition: All 0.3s ease-in-out;
background: #f4f4f4;
p {
color: #424242;
}
}
}
}
}
.action {
margin-top: 15px;
text-align: right;
position: relative;
}
}
.am-cf:after {
clear: both;
}
.am-cf:after,
.am-cf:before {
content: ' ';
display: table;
}
</style>
二、中间组件
1.根据默认参数渲染组件
<template>
<div class="my-phone">
<div class="phone-top optional" :style="{backgroundColor: pages.page.style.titleBackgroundColor}" @click="changitem(pages.page,-1)" :class="{'selected':selectitem.type=='page'}">
<h4 :style="{color: pages.page.style.titleTextColor}">{{pages.page.params.title}}</h4>
</div>
<div class="phone-main">
<div class="dragArea" :style="{background:pages.page.style.pageBackgroundColor}">
<el-form size="mini" label-position="right" label-width="90px" class="my-form">
<draggable v-model="pages.items" @start="dragstart" @end="dragend">
<div v-for="(item,index) in pages.items" :key="index" @click="changitem(item,index)" class="drag optional" :class="{selected:selectindex==index}">
<template v-if="item.type=='input'">
<el-form-item :label="item.name">
<el-input v-model="item.params.defaultvalue" :placeholder="item.params.placeholder"></el-input>
</el-form-item>
</template>
<template v-if="item.type=='textarea'">
<el-form-item :label="item.name">
<el-input v-model="item.params.defaultvalue" :placeholder="item.params.placeholder" type="textarea"></el-input>
</el-form-item>
</template>
<template v-if="item.type=='select'">
<el-form-item :label="item.name">
<el-select v-model="item.params.defaultvalue" :placeholder="item.params.placeholder" style="width:100%">
<el-option v-for="(items,index) in item.options" :key="index" :label="items.label" :value="items.value">
</el-option>
</el-select>
</el-form-item>
</template>
<template v-if="item.type=='radio'">
<el-form-item :label="item.name">
<el-radio-group v-model="item.params.defaultvalue" v-if="item.type=='radio'">
<el-radio :label="item.value" v-for="(items,index) in item.options" :key="index">{{items.label}}</el-radio>
</el-radio-group>
</el-form-item>
</template>
<template v-if="item.type=='checkbox'">
<el-form-item :label="item.name">
<el-checkbox-group v-model="item.params.defaultvalue" v-if="item.type=='checkbox'">
<el-checkbox :label="items.value" v-for="(items,index) in item.options" :key="index"></el-checkbox>
</el-checkbox-group>
</el-form-item>
</template>
<template v-if="item.type=='date'">
<el-form-item :label="item.name">
<el-date-picker v-model="item.params.defaultvalue" :type="item.params.type" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" style="width:100%" v-if="item.type=='date'">
</el-date-picker>
</el-form-item>
</template>
<template v-if="item.type=='fileupload'">
<el-form-item :label="item.name">
<el-button type="primary" v-if="item.type=='fileupload'">上传文件</el-button>
</el-form-item>
</template>
<template v-if="item.type=='imgupload'">
<el-form-item :label="item.name">
<div style="width: 100px; height: 100px;">
<el-upload class="imgupload" action="https://jsonplaceholder.typicode.com/posts/" style="width: 100px; height: 100px;" list-type="picture-card">
<i class="el-icon-plus"></i>
</el-upload>
</div>
</el-form-item>
</template>
<div v-if="item.type=='text'" :style="{color:item.params.style.textColor,height:'30px'}">{{item.params.defaultvalue||'默认文字'}}</div>
<div v-if="item.type=='guide'" class="my-guide" :style="{padding:item.params.style.paddingTop+'px 0px',background:item.params.style.background}">
<p class="line" :style="{borderTop:item.params.style.lineHeight+'px '+item.params.style.lineStyle+' '+item.params.style.lineColor}"></p>
</div>
<div v-if="item.type=='dynamictable'" class="dynamictable" draggable="false">
<div class="answer_title">
{{item.name}}
</div>
<div class="q-auto-table-row" v-for="(count,index) in item.params.min" :key="index">
<div class="topic_bel option_label row-count">{{index+1}}</div>
<ul class="data_row">
<li v-for="(i,index) in item.options" :key="index">
<div class="optionBox_t">
{{i.label}}
</div>
<div class="optionBox_c">
<el-input :disabled="true"></el-input>
</div>
</li>
</ul>
</div>
<div class="add-a-line-btn">+ 继续填写</div>
</div>
<el-form-item v-if="item.params.tip">
{{item.params.tip}}
</el-form-item>
<div class="btn-edit-del">
<div class="btn-edit-del" @click.stop="delitem(item,index)">
删除
</div>
</div>
</div>
</draggable>
</el-form>
</div>
</div>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: '',
model: {
prop: 'pages',
},
props: {
pages: {
type: [Array, Object],
default() {
return {
page: {
type: 'page',
name: '页面设置',
params: {
title: '',
share_title: '',
},
style: {
titleTextColor: '',
titleBackgroundColor: '',
pageBackgroundColor: '',
},
},
items: [],
}
},
},
},
data() {
return {
selected: '',
selectindex: -1,
selectitem: {
type: '',
},
}
},
components: {
draggable,
},
created() {},
methods: {
//选中模块
changitem(val, index) {
this.selectitem = val
this.selectindex = index
this.chang()
},
dragstart(evt) {
this.selectindex = evt.oldIndex
this.chang()
},
dragend(evt) {
this.selectindex = evt.newIndex
this.chang()
},
delitem(item, index) {
this.$confirm('确认删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
this.pages.items.splice(index, 1)
this.selected = ''
this.$emit('modulechang', { type: '' })
})
.catch(() => {})
},
chang() {
this.$emit('modulechang', this.selectitem)
},
},
}
</script>
<style lang="scss" scoped>
.my-phone {
width: 377px;
border-radius: 3px;
box-shadow: 0 3px 10px #dcdcdc;
border: 1px solid #ddd;
.phone-top {
width: 100%;
height: 66px;
text-align: center;
background: url('../../assets/phone-top-black.png') center center / contain
no-repeat;
h4 {
margin: 0;
font-size: 1.4rem;
font-weight: normal;
white-space: nowrap;
line-height: 88px;
}
}
.optional {
position: relative;
&:before {
content: ' ';
}
}
.optional:hover:before {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border: 2px dashed #00a0e9;
cursor: move;
}
.optional.selected:before {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
border: 2px dashed #00a0e9;
cursor: move;
}
.phone-main {
position: relative;
border-top: 0;
user-select: none;
line-height: normal;
.my-service {
position: absolute;
z-index: 999;
.service-icon {
padding: 5px;
img {
display: block;
width: 45px;
height: 45px;
}
}
}
.dragArea {
overflow-y: auto;
height: 580px;
.drag {
&:hover {
.btn-edit-del {
display: block;
}
}
.btn-edit-del {
height: 16px;
position: absolute;
right: 2px;
bottom: 2px;
display: none;
z-index: 90;
> div {
width: 32px;
height: 16px;
line-height: 16px;
display: inline-block;
text-align: center;
font-size: 10px;
color: #fff;
background: rgba(0, 0, 0, 0.4);
margin-left: 2px;
cursor: pointer;
position: relative;
z-index: 11;
}
}
}
.selected {
.btn-edit-del {
display: block;
}
}
}
}
}
.my-form {
.el-form-item {
padding: 10px;
margin: 0 10px;
position: relative;
&:after {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
display: block;
z-index: 8;
content: ' ';
cursor: move;
}
}
}
.dynamictable {
z-index: -1;
padding: 10px;
.answer_title {
font-size: 15px;
text-indent: 10px;
}
.add-a-line-btn {
margin: 10px 0;
padding: 0;
height: 35px;
border-radius: 2px;
border: 1px dashed #d8d8d8;
background-color: #fff;
font-size: 14px;
line-height: 35px;
color: #2672ff;
text-align: center;
cursor: pointer;
}
.q-auto-table-row {
.topic_bel {
text-indent: 20px;
height: 20px;
line-height: 20px;
}
.data_row {
background: #f7f8fa;
border-radius: 4px;
padding: 10px;
margin: 10px;
position: relative;
li {
list-style: none;
}
}
}
}
</style>
<style>
.imgupload .el-upload--picture-card {
width: 100px;
height: 100px;
line-height: 100px;
}
</style>
三、右边组件
1.配置组件参数
<template>
<div class="my-editor form-horizontal" v-show="data.type">
<!-- 页面设置 -->
<template v-if="data.type=='page'">
<div class="editor-title">
页面设置
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="页面标题">
<el-input v-model="data.params.title"></el-input>
<span class="help-block">小程序端顶部显示的标题</span>
</el-form-item>
<el-form-item label="标题栏文字">
<el-radio-group v-model="data.style.titleTextColor">
<el-radio label="black">黑色</el-radio>
<el-radio label="white">白色</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="标题栏背景">
<el-color-picker v-model="data.style.titleBackgroundColor"></el-color-picker>
</el-form-item>
<el-form-item label="页面背景">
<el-color-picker v-model="data.style.pageBackgroundColor"></el-color-picker>
</el-form-item>
</el-form>
</template>
<!-- 单行文本 -->
<template v-if="data.type=='input'">
<div class="editor-title">
单行文本
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="字段标识">
<el-input v-model="data.key"></el-input>
</el-form-item>
<el-form-item label="标题">
<el-input v-model="data.name"></el-input>
</el-form-item>
<el-form-item label="占位内容">
<el-input v-model="data.params.placeholder"></el-input>
</el-form-item>
<el-form-item label="默认值">
<el-input v-model="data.params.defaultvalue"></el-input>
</el-form-item>
<el-form-item label="提示文字">
<el-input v-model="data.params.tip"></el-input>
</el-form-item>
<el-form-item label="校验">
<div>
<el-checkbox v-model="rules.required">必填</el-checkbox>
<el-input v-if="rules.required" style="margin-left: 24px;padding-right:24px" v-model="rules.requiredMessage" placeholder="自定义错误提示"></el-input>
</div>
<div>
<el-checkbox v-model="rules.dataTypeCheck">
<el-select v-model="rules.dataType" placeholder="请选择" :disabled="!rules.dataTypeCheck">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-checkbox>
<el-input v-if="rules.dataTypeCheck" style="margin-left: 24px;padding-right:24px" v-model="rules.dataTypeMessage" placeholder="自定义错误提示"></el-input>
</div>
<div>
<el-checkbox v-model="rules.patternCheck">
<el-input v-model="rules.pattern" placeholder="填写正则表达式" :disabled="!rules.patternCheck"></el-input>
</el-checkbox>
<el-input v-if="rules.patternCheck" style="margin-left: 24px;padding-right:24px" v-model="rules.patternMessage" placeholder="自定义错误提示"></el-input>
</div>
</el-form-item>
</el-form>
</template>
<!-- 多行文本 -->
<template v-if="data.type=='textarea'">
<div class="editor-title">
多行文本
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="字段标识">
<el-input v-model="data.key"></el-input>
</el-form-item>
<el-form-item label="标题">
<el-input v-model="data.name"></el-input>
</el-form-item>
<el-form-item label="占位内容">
<el-input v-model="data.params.placeholder"></el-input>
</el-form-item>
<el-form-item label="默认值">
<el-input v-model="data.params.defaultvalue"></el-input>
</el-form-item>
<el-form-item label="提示文字">
<el-input v-model="data.params.tip"></el-input>
</el-form-item>
<el-form-item label="校验">
<div>
<el-checkbox v-model="rules.required">必填</el-checkbox>
<el-input v-if="rules.required" style="margin-left: 24px;padding-right:24px" v-model="rules.requiredMessage" placeholder="自定义错误提示"></el-input>
</div>
<div>
<el-checkbox v-model="rules.patternCheck">
<el-input v-model="rules.pattern" placeholder="填写正则表达式" :disabled="!rules.patternCheck"></el-input>
</el-checkbox>
<el-input v-if="rules.patternCheck" style="margin-left: 24px;padding-right:24px" v-model="rules.patternMessage" placeholder="自定义错误提示"></el-input>
</div>
</el-form-item>
</el-form>
</template>
<!-- 下拉选择框 -->
<template v-if="data.type=='select'">
<div class="editor-title">
下拉选择框
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="字段标识">
<el-input v-model="data.key"></el-input>
</el-form-item>
<el-form-item label="标题">
<el-input v-model="data.name"></el-input>
</el-form-item>
<el-form-item label="占位内容">
<el-input v-model="data.params.placeholder"></el-input>
</el-form-item>
<el-form-item label="提示文字">
<el-input v-model="data.params.tip"></el-input>
</el-form-item>
<el-form-item label="选项">
<div v-for="(item,index) in data.options" :key="index" class="slectoptions">
<div>
<el-input v-model="item.value" style="width:100%"></el-input>
</div>
<div>
<i class="el-icon-remove-outline" @click="del(data.options,index)"></i>
</div>
</div>
<div>
<el-link type="primary" :underline="false" @click="additem(data.options)">添加选项</el-link>
</div>
</el-form-item>
<el-form-item label="校验">
<div>
<el-checkbox v-model="rules.required">必填</el-checkbox>
<el-input v-if="rules.required" style="margin-left: 24px;padding-right:24px" v-model="rules.requiredMessage" placeholder="自定义错误提示"></el-input>
</div>
</el-form-item>
</el-form>
</template>
<!-- 单选框 -->
<template v-if="data.type=='radio'">
<div class="editor-title">
单选框
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="字段标识">
<el-input v-model="data.key"></el-input>
</el-form-item>
<el-form-item label="标题">
<el-input v-model="data.name"></el-input>
</el-form-item>
<el-form-item label="选项">
<div v-for="(item,index) in data.options" :key="index" class="slectoptions">
<div>
<el-input v-model="item.value" style="width:100%"></el-input>
</div>
<div>
<i class="el-icon-remove-outline" @click="del(data.options,index)"></i>
</div>
</div>
<div>
<el-link type="primary" :underline="false" @click="additem(data.options)">添加选项</el-link>
</div>
</el-form-item>
<el-form-item label="提示文字">
<el-input v-model="data.params.tip"></el-input>
</el-form-item>
<el-form-item label="校验">
<div>
<el-checkbox v-model="rules.required">必填</el-checkbox>
<el-input v-if="rules.required" style="margin-left: 24px;padding-right:24px" v-model="rules.requiredMessage" placeholder="自定义错误提示"></el-input>
</div>
</el-form-item>
</el-form>
</template>
<!-- 多选框 -->
<template v-if="data.type=='checkbox'">
<div class="editor-title">
多选框
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="字段标识">
<el-input v-model="data.key"></el-input>
</el-form-item>
<el-form-item label="标题">
<el-input v-model="data.name"></el-input>
</el-form-item>
<el-form-item label="提示文字">
<el-input v-model="data.params.tip"></el-input>
</el-form-item>
<el-form-item label="选项">
<div v-for="(item,index) in data.options" :key="index" class="slectoptions">
<div>
<el-input v-model="item.value" style="width:100%"></el-input>
</div>
<div>
<i class="el-icon-remove-outline" @click="del(data.options,index)"></i>
</div>
</div>
<div>
<el-link type="primary" :underline="false" @click="additem(data.options)">添加选项</el-link>
</div>
</el-form-item>
<el-form-item label="校验">
<div>
<el-checkbox v-model="rules.required">必填</el-checkbox>
<el-input v-if="rules.required" style="margin-left: 24px;padding-right:24px" v-model="rules.requiredMessage" placeholder="自定义错误提示"></el-input>
</div>
</el-form-item>
</el-form>
</template>
<!-- 日期选择 -->
<template v-if="data.type=='date'">
<div class="editor-title">
日期选择
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="字段标识">
<el-input v-model="data.key"></el-input>
</el-form-item>
<el-form-item label="标题">
<el-input v-model="data.name"></el-input>
</el-form-item>
<el-form-item label="占位内容">
<el-input v-model="data.params.placeholder"></el-input>
</el-form-item>
<el-form-item label="提示文字">
<el-input v-model="data.params.tip"></el-input>
</el-form-item>
<el-form-item label="时间类型">
<el-select v-model="data.params.type" placeholder="请选择">
<el-option v-for="item in Timeoptions" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="校验">
<div>
<el-checkbox v-model="rules.required">必填</el-checkbox>
<el-input v-if="rules.required" style="margin-left: 24px;padding-right:24px" v-model="rules.requiredMessage" placeholder="自定义错误提示"></el-input>
</div>
</el-form-item>
</el-form>
</template>
<!-- 文字 -->
<template v-if="data.type=='text'">
<div class="editor-title">
文字
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="字段标识">
<el-input v-model="data.key"></el-input>
</el-form-item>
<el-form-item label="默认值">
<el-input v-model="data.params.defaultvalue"></el-input>
</el-form-item>
<el-form-item label="文字颜色">
<el-color-picker v-model="data.params.style.textColor"></el-color-picker>
</el-form-item>
</el-form>
</template>
<!-- 文件 -->
<template v-if="data.type=='fileupload'">
<div class="editor-title">
文件
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="字段标识">
<el-input v-model="data.key"></el-input>
</el-form-item>
<el-form-item label="标题">
<el-input v-model="data.name"></el-input>
</el-form-item>
<el-form-item label="提示文字">
<el-input v-model="data.params.tip"></el-input>
</el-form-item>
<el-form-item label="上传文件数量">
<el-input-number v-model="data.params.limit" controls-position="right" :min="1" :max="10"></el-input-number>
</el-form-item>
<el-form-item label="校验">
<div>
<el-checkbox v-model="rules.required">必填</el-checkbox>
<el-input v-if="rules.required" style="margin-left: 24px;padding-right:24px" v-model="rules.requiredMessage" placeholder="自定义错误提示"></el-input>
</div>
</el-form-item>
</el-form>
</template>
<!-- 图片 -->
<template v-if="data.type=='imgupload'">
<div class="editor-title">
图片
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="字段标识">
<el-input v-model="data.key"></el-input>
</el-form-item>
<el-form-item label="标题">
<el-input v-model="data.name"></el-input>
</el-form-item>
<el-form-item label="提示文字">
<el-input v-model="data.params.tip"></el-input>
</el-form-item>
<el-form-item label="上传文件数量">
<el-input-number v-model="data.params.limit" controls-position="right" :min="1" :max="10"></el-input-number>
</el-form-item>
<el-form-item label="校验">
<div>
<el-checkbox v-model="rules.required">必填</el-checkbox>
<el-input v-if="rules.required" style="margin-left: 24px;padding-right:24px" v-model="rules.requiredMessage" placeholder="自定义错误提示"></el-input>
</div>
</el-form-item>
</el-form>
</template>
<!-- 辅助线 -->
<template v-if="data.type=='guide'">
<div class="editor-title">
辅助线
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="背景颜色">
<el-color-picker v-model="data.params.style.background"></el-color-picker>
</el-form-item>
<el-form-item label="线条样式">
<el-radio-group v-model="data.params.style.lineStyle">
<el-radio label="solid">实线</el-radio>
<el-radio label="dashed">虚线</el-radio>
<el-radio label="dotted">点状</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="线条颜色">
<el-color-picker v-model="data.params.style.lineColor"></el-color-picker>
</el-form-item>
<el-form-item label="线条高度">
<el-slider v-model="data.params.style.lineHeight" :min="0" :max="20"></el-slider>{{data.params.style.lineHeight}}px(像素)
</el-form-item>
<el-form-item label="上下边距">
<el-slider v-model="data.params.style.paddingTop" :min="0" :max="50"></el-slider>{{data.params.style.paddingTop}}px(像素)
</el-form-item>
</el-form>
</template>
<!-- 自增表格 -->
<template v-if="data.type=='dynamictable'">
<div class="editor-title">
自增表格
</div>
<el-form label-width="100px" size="mini">
<el-form-item label="字段标识">
<el-input v-model="data.key"></el-input>
</el-form-item>
<el-form-item label="标题">
<el-input v-model="data.name"></el-input>
</el-form-item>
<el-form-item label="表格默认行数">
<el-input-number v-model="data.params.min" controls-position="right" :min="1" :max="20"></el-input-number>
</el-form-item>
<div class="form-items">
<draggable v-model="data.options">
<div class="form-item" v-for="(img,index) in data.options" :key="'banner'+index">
<i class="el-icon-close item-delete" @click.stop="del(data.options,index)"></i>
<div class="item-inner">
<el-form-item label="标题">
<el-input v-model="img.label"></el-input>
</el-form-item>
<el-form-item label="类型">
<el-select v-model="img.value" placeholder="请选择">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value">
</el-option>
<el-option label="下拉单选" value="select">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="选项设置">
<el-input v-model="img.options" :disabled="img.value=='select'?false:true"></el-input>
</el-form-item>
<el-form-item label="必填">
<el-checkbox v-model="img.required">必填</el-checkbox>
</el-form-item>
</div>
</div>
</draggable>
</div>
<div class="form-item-add" @click.stop="addimgitem(data.options)">
添加一个
</div>
</el-form>
</template>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
name: '',
model: {
prop: 'data',
},
props: {
data: {
type: [Object, Array],
default() {
return {
type: '',
}
},
},
},
data() {
return {
options: [
{ value: 'string', label: '字符串' },
{ value: 'number', label: '数字' },
{ value: 'boolean', label: '布尔值' },
{ value: 'integer', label: '整数' },
{ value: 'float', label: '浮点数' },
{ value: 'url', label: 'url地址' },
{ value: 'email', label: '邮箱地址' },
{ value: 'phone', label: '手机号' },
{ value: 'idcard', label: '身份证' },
],
Timeoptions: [
{ value: 'date', label: '日' },
{ value: 'year', label: '年' },
{ value: 'month', label: '月' },
{ value: 'daterange', label: '范围' },
],
checkList: [],
rules: {
required: false,
requiredMessage: '',
dataType: '',
dataTypeCheck: false,
dataTypeMessage: '',
pattern: '',
patternCheck: false,
patternMessage: '',
},
}
},
components: {
draggable,
},
watch: {
'data.key'(val, oldval) {
this.typechang(val)
},
rules: {
handler(val, oldval) {
let r = []
if (val.required) {
r.push({
required: true,
message: val.requiredMessage,
})
}
if (val.dataTypeCheck && val.dataType) {
r.push({
type: val.dataType,
message: val.dataTypeMessage,
})
}
if (val.patternCheck && val.pattern) {
r.push({
pattern: val.pattern,
message: val.patternMessage,
})
}
this.data.rules = r
},
deep: true,
},
},
created() {},
methods: {
typechang(val) {
switch (val) {
case 'input':
this.rules = {
required: false,
requiredMessage: '',
dataType: '',
dataTypeCheck: false,
dataTypeMessage: '',
pattern: '',
patternCheck: false,
patternMessage: '',
}
break
case 'textarea':
this.rules = {
required: false,
requiredMessage: '',
pattern: '',
patternCheck: false,
patternMessage: '',
}
break
case 'text':
this.rules = {}
break
default:
this.rules = {
required: false,
requiredMessage: '',
}
break
}
if (this.data.rules) {
this.data.rules.map(m => {
if (m.required) {
this.rules.required = true
this.rules.requiredMessage = m.message
} else {
this.rules.required = false
this.rules.requiredMessage = ''
}
if (m.type) {
this.rules.dataType = m.type
this.rules.dataTypeMessage = m.message
this.rules.dataTypeCheck = true
} else {
this.rules.dataType = ''
this.rules.dataTypeMessage = ''
this.rules.dataTypeCheck = false
}
if (m.pattern) {
this.rules.pattern = m.pattern
this.rules.patternMessage = m.message
this.rules.patternCheck = true
} else {
this.rules.pattern = ''
this.rules.patternMessage = ''
this.rules.patternCheck = false
}
})
}
},
additem(data) {
data.push({
label: '新选项',
value: '新选项',
})
},
del(data, index) {
data.splice(index, 1)
},
addimgitem(data) {
data.push({
label: '新标题',
value: 'string',
required: true,
options: '',
key: `dynamictable_dy_${data.length}_${new Date().getTime()}`,
placeholder: '',
})
},
},
}
</script>
<style lang="scss" scoped>
.my-editor {
width: 400px;
height: auto;
min-height: 100px;
padding: 15px 10px;
border: 1px solid #ddd;
.data-image {
display: inline-block;
width: auto;
min-width: 40px;
max-width: 220px;
text-align: center;
cursor: pointer;
}
.editor-title {
position: relative;
padding: 0 22px;
height: 34px;
border-bottom: 1px solid #eef1f5;
margin-bottom: 20px;
line-height: 34px;
&:before {
content: '';
position: absolute;
width: 4px;
height: 13px;
background: #00aeff;
top: 10px;
left: 9px;
}
}
.tpl-form-line-form {
font-size: 1.3rem !important;
color: #656565;
}
.help-block {
color: #838fa1;
font-size: 12px;
}
.form-items {
height: auto;
padding: 5px 6px;
.form-item {
background: #f7fafc;
margin-bottom: 0.6rem;
position: relative;
border-radius: 3px;
cursor: move;
.item-delete {
position: absolute;
top: -6px;
right: -6px;
height: 16px;
width: 16px;
line-height: 16px;
background: rgba(153, 153, 153, 0.7);
color: #fff;
border-radius: 50%;
text-align: center;
cursor: pointer;
font-size: 12px;
transition: background-color 0.3s ease-out, border-color 0.3s ease-out;
}
.item-inner {
padding: 10px;
background: #f7fafc;
span {
display: block;
width: 100%;
}
}
}
}
.form-item-add {
width: 100%;
background: #fdfdfd;
color: #6b6b6b;
border: 1px solid #efefef;
outline: none;
padding: 10px 16px;
border-radius: 2px;
font-size: 12px;
line-height: 1;
text-align: center;
vertical-align: middle;
cursor: pointer;
user-select: none;
transition: All 0.2s ease-in-out;
}
.data-image {
display: inline-block;
width: auto;
min-width: 40px;
max-width: 220px;
text-align: center;
cursor: pointer;
img {
display: block;
max-width: 100%;
height: 50px;
}
}
}
.slectoptions {
display: flex;
margin: 10px 0;
div:first-child {
flex: 1;
margin-right: 10px;
}
div:last-child {
width: 20px;
&:hover {
cursor: pointer;
}
}
}
.slectoptions:first-child {
margin-top: 0px;
}
</style>
四、主组件
<template>
<el-row :gutter="20">
<el-col :span="6">
<div class="form-name">
<el-input placeholder="请输入表单名称" v-model="formname"></el-input>
</div>
<LeftPage @save="save" @add="addcomponent"></LeftPage>
</el-col>
<el-col :span="9">
<MainPage :pages="pages" @modulechang="modulechang"></MainPage>
</el-col>
<el-col :span="9">
<RightPage :data="selectitem"></RightPage>
</el-col>
</el-row>
</template>
<script>
import LeftPage from './left'
import MainPage from './main'
import RightPage from './right'
export default {
name: '',
props: {
data: {
type: [Object],
default() {
return {}
},
},
name: {
type: [String],
default: '',
},
},
data() {
return {
pages: this.data,
selectitem: {
type: '',
},
formname: this.name,
}
},
components: {
LeftPage,
MainPage,
RightPage,
},
created() {},
watch: {
'data.page.type'() {
this.pages = this.data
},
name(val, oldval) {
this.formname = val
},
},
methods: {
modulechang(val) {
this.selectitem = val
},
addcomponent(item) {
this.pages.items.push(item)
},
save() {
this.$emit('save', {
name: this.formname,
...this.pages,
})
},
},
}
</script>
<style lang="scss" scoped>
.form-name {
width: 260px;
margin: 20px 0;
}
</style>
下期会分享小程序表单渲染
版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 作者: bilimumu 原文链接:https://juejin.im/post/6859904161576812557