vue+elementUI实现自定义表单

386 阅读4分钟

本文介绍了基于vue+elementui实现自定义表单,左侧字段,中间绘制表单项,右侧是相应控件修改。点击字段添加到中间表单,表单项和右侧select或radio类的内容选项排序通过vuedraggable实现。

效果图


111.png

微信截图_20240821150616.png 以下是html代码片段

<template>
  <div>
    <el-row :gutter="20" class="container">
      <!-- 左侧字段 -->
      <el-col :span="5" class="left-side">
        <div class="grid-content bg-purple">
          <p class="title">基础字段</p>
          <div class="leftMenu">
            <div v-for="(item,index) in leftMenu" :key="item.id" class="item" @click="addItem(item,index)">
              <el-button size="small">{{ item.label }}</el-button>
            </div>
          </div>
        </div>
      </el-col>
      <!-- 表单项 -->
      <el-col :span="14" class="center">
        <template v-if=" 0 == (formList &&formList.length)">
          <div class="tips">点击左边的字段进行自定义编辑</div>
        </template>
        <!-- style="height:calc(100vh - 220px);overflow-y:auto" -->
        <!-- <el-form class="forms-content" :model="ruleForm"  :rules="rules"> -->
          <draggable
            group="comp"
            :options="{animation: 250}"
            v-model="formList"
            @end="end"
          >
            <div
              class="temp-content"
              @click="activeIndex=index"
              :class="activeIndex==index?'temp-active':''"
              v-for="(item, index) in formList"
              :key="index"
            >
              <!-- {{item}} -->
              <div style="padding:5px 3px 10px 3px">
                <el-row style="display:flex;">
                  <el-col :span="23" class="label">
                    <span v-if="item.required" style="color:#ff4949; font-size:14px;">*</span>
                    {{item.label}}
                  </el-col>
                  <el-col :span="1" class="text-right pointer iconFont">
                    <template v-if="activeIndex==index">
                      <i class="el-icon-delete" @click.stop="delItem(index)"></i>
                    </template>
                  </el-col>
                </el-row>
              </div>
              <div>
                <!-- 单选 -->
                <template v-if="item.type=='radio'">
                  <el-radio-group v-model="item.value">
                    <el-radio v-for="(it, idx) in item.options" :key="idx" :label="idx">{{it}}</el-radio>
                  </el-radio-group>
                </template>
                <!-- 下拉框 -->
                <template v-else-if="item.type=='select'">
                  <el-select v-model="item.value" clearable :placeholder="'请选择'+item.label" size="small" style="width:100%;">
                    <el-option
                      v-for="(it, idx) in item.options"
                      :key="idx"
                      :label="it"
                      :value="idx">
                    </el-option>
                  </el-select>
                </template>
                <!-- 文本框 -->
                <template v-else>
                  <el-input size="small" clearable v-model="item.value" :placeholder="item.placeholder?item.placeholder:'请输入'+item.label"></el-input>
                </template>
              </div>
            </div>
          </draggable>
        <!-- </el-form> -->
      </el-col>
      <!-- 右侧自定义内容 -->
      <el-col :span="5" class="right-side">
        <template v-if="formList&&formList.length>0">
          <!-- label-suffix=":" -->
          <el-form class="forms" style="margin-top:10px">
            <el-form-item label="字段名称">
              <el-input clearable v-model="formList[activeIndex].label" size="small"></el-input>
            </el-form-item>
            <template v-if="formList[activeIndex].type != 'radio'">
              <el-form-item label="默认内容">
                <!-- 自定义选项 -->
                <el-select v-model="dealValue" size="small" style="width:100%; margin-bottom:3px;">
                  <el-option
                    v-for="item in dealOptions"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value">
                  </el-option>
                </el-select>
                <!-- 默认内容 -->
                <el-input v-model="formList[activeIndex].value" clearable placeholder="请输入默认内容" size="small"></el-input>
              </el-form-item>
            </template>
            <!-- 选项 -->
            <template v-if="formList[activeIndex].type == 'radio' || formList[activeIndex].type == 'select'">
              <el-form-item label="内容选项">
                <draggable
                    group="comptwo"
                    :options="{animation: 50}"
                    v-model="formList[activeIndex].options"
                    @end="end2"
                  >
                        <el-row
                          v-for="(item, index) in formList[activeIndex].options"
                          :key="index"
                        >
                          <el-col :span="2" class="iconFont" style="margin-right:8px">
                            <i class="el-icon-s-operation operation"></i>
                          </el-col>
                          <el-col :span="18">
                            <el-input clearable v-model="formList[activeIndex].options[index]" size="small"></el-input>
                          </el-col>
                          <el-col :span="2" class="iconFont" style="margin-left:8px">
                            <i class="el-icon-delete delete" @click="delOption(index)"></i>
                          </el-col>
                        </el-row>
                </draggable>
                <div>
                  <el-button style="width:100%" size="small" @click="addOption"><i class="el-icon-plus"></i>添加新选项</el-button>
                </div>
              </el-form-item>
            </template>
            <!-- 是否必填项 -->
            <el-form-item label="是否必填"  class="form-item">
              <el-switch v-model="formList[activeIndex].required"></el-switch>
            </el-form-item>
            <!-- 文本框的字数 -->
            <el-form-item label="字数" v-if="formList[activeIndex].type == 'text' && formList[activeIndex].required == true" style="text-align:center; margin-top:0;">
              <el-input size="small" clearable v-model.number="formList[activeIndex].numStart" placeholder="不限" autocomplete="off" style="width:40%;"></el-input>
              -
              <el-input size="small" clearable v-model.number="formList[activeIndex].numEnd" placeholder="不限" autocomplete="off" style="width:40%;"></el-input>
            </el-form-item>
          </el-form>
        </template>
      </el-col>
    </el-row>
    <el-row :gutter="20" class="btn">
      <el-button size="small">取消</el-button>
      <el-button size="small" type="primary" @click="getFields">提交</el-button>
    </el-row>
  </div>
</template>

以下是script代码片段
vuedraggable安装语句npm install vuedraggable

<script>
import draggable from 'vuedraggable'
export default {
   components:{
    draggable
  },
  data() {
    return {
      dealValue:1,
      dealOptions:[{label:'自定义', value:1}],
      leftMenu: [
        {id: "1", label: "名称", type: 'text'},
        {id: "2", label: "性别", type: 'radio'},
        {id: "3", label: "年龄", type: 'text'},
        {id: "4", label: "手机号", type: 'text'},
        {id: "5", label: "身份证号", type: 'select'}
      ],
      activeIndex: 0, // 当前编辑的 index
      formList: []
    }
  },
  methods: {
    // 获取字段数据
    getFields() {
      if (0 == this.formList.length) {
        this.$message.error('需增加模板字段!')
        return false
      }
      let pass = true
      let formList = this.formList
      for (let i = 0; i < formList.length; i++) {
        formList[i]._vModel = 'field' + i
        if (!formList[i].label) {
          this.activeIndex = i;
          this.$message.error('请输入字段名称!')
          pass = false
          break
        }
        if (!formList[i].value && formList[i].required) {
          this.activeIndex = i;
          if(formList[i].type == 'radio' || formList[i].type == 'select' ){
            this.$message.error(`请选择${formList[i].label}!`);
          } else {
            this.$message.error(`请输入${formList[i].label}!`);
          }
          pass = false
          break
        }
        if (formList[i].type == 'text') {
          if(formList[i].numStart == "" || formList[i].value.length >= formList[i].numStart){
            if(formList[i].numEnd == "" || formList[i].value.length <= formList[i].numEnd){} else {
              this.$message.error(`${formList[i].label}内容需小于${formList[i].numEnd}字!`);
              pass = false
              break
            }
          } else {
            this.$message.error(`${formList[i].label}内容需大于${formList[i].numStart}字!`);
            pass = false
            break
          }
        }
      }
      //
      if (pass) {
        console.log(this.formList,'zr')
        return this.formList
      } else {
        return false
      }
    },
    // 新增选项
    addOption() {
      let idx = this.formList[this.activeIndex].options.length + 1
      this.formList[this.activeIndex].options.push('选项' + idx)
    },
    // 删除选项
    delOption(index) {
      this.formList[this.activeIndex].options.splice(index, 1)
    },
    // 删除模板字段
    delItem(index) {
      this.formList.splice(index, 1)
      let leg = this.formList.length
      if (leg == index && leg > 0) {
        this.activeIndex = index - 1
      }
    },
    // 根据类型获取字段
    getFormByType(data) {
      let item = {
        id:data.id,//唯一值
        label: data.label,
        required: false,
        type: data.type,
        placeholder: '',
        value:''
      }
      if(data.type == 'text'){
        item.numStart = '';
        item.numEnd = '';
      }
      // 是否单选项
      if (data.type == 'radio') {
        item.options = ['选项1', '选项2']
      }
      // 是否下拉选项
      if (data.type == 'select') {
        item.options = ['选项1', '选项2']
      }
      return item
    },
    //新增模板字段
    addItem(item){
      if(JSON.stringify(this.formList).indexOf(`"id":"${item.id}"`) > -1){return false;}//已存在则不重复
      let data = this.getFormByType(item)
      this.activeIndex = this.formList.length; //放入的位置,按顺序排下来
      this.formList.splice(this.formList.length, 0, data);
    },
    end(e) {
      // 判断移动的项目是否为编辑行
      if (e.oldIndex != e.newIndex && this.activeIndex == e.oldIndex) {
        this.activeIndex = e.newIndex
      }
      console.log(this.formList)
    },
    end2(e) {
      console.log(this.formList[this.activeIndex].options)
    },
  }
}
</script>

以下是css样式片段

<style scoped>
.btn{
  text-align: center;
  margin:0;
}
.text-red{
  color:#f56c6c
}
.iconFont i{
  font-size:17px;
}
.forms .el-form-item {
  margin-bottom:10px;
}
.forms .label{
  font-weight: normal;
  font-size:14px;
}
>>>.forms .el-form-item:first-child{
  margin-top:0;
}
>>>.forms .el-form-item__label{
  line-height:normal !important;
  margin-bottom:10px;
  font-weight: normal;
  width:100%;
  text-align: left;
}
>>>.forms .form-item .el-form-item__label{
  line-height:40px !important;
  width:auto;
}
.forms .el-form-item{
  margin-top:20px;
}
.forms .el-input{
  font-size:13px;
}
.text-right{
  text-align:right;
}
.text-right:hover, .delete:hover{
  cursor: pointer;
  /* color:#f56c6c; */
}
.label{
  font-size: 14px;
}
.tips{
  line-height: 50px;
  height: 50px;
  border: 1px dashed #d9d9d9;
  padding-left:20px;
  margin-top:10px;
}
.container {
  display: flex; /* 启用Flex布局 */
  padding:20px;
}
.title{
  font-size: 14px;
  margin-top:5px;
  margin-bottom:15px;
  padding-left:5px;
}
.left-side, .right-side {
  /* width: 245px; */
  width: 24%;
  border:1px solid #ccc;
  border-radius: 5px;
  padding:10px 6px;
}
.leftMenu{
  display: flex;
  flex-wrap: wrap; /* 可换行 */
  text-align: center;
}
.leftMenu .item{
  width:50%;
  margin-bottom:10px;
}
.leftMenu .item button{
  width:90%;
  text-align: center;
}
.leftMenu .item button.special{
  padding-left:0;
  padding-right:0;
}
.center {
  flex-grow: 1; /* 中间部分自适应剩余空间 */
  min-width: 200px;
  /* border:1px solid #ccc; */
  /* border-radius: 5px; */
  margin:0 15px;
  padding:0px !important;
}
.temp-content{
  margin-bottom:10px;
  padding:10px;
  border-radius: 6px;
  /* background-color: #f6f6f6; */
  border:1px solid #DCDFE6;
}
.temp-active{
  background-color: #ecf5ff;
}
.temp-content:hover, .operation:hover, >>>.right-side .el-row{
  cursor: move;
}
</style>