vue动态配置小程序表单页面(一)__Vue.js
发布于 4 年前 作者 banyungong 1390 次浏览 来自 分享
粉丝福利 : 关注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

回到顶部