封装 element-ui 的 Form表单

2021/08/13更新,添加了 placeholder 自定义提示

# 新建 Form.vue

src/components/Form.vue

<template>
  <div>
    <el-form
      ref="form"
      :model="form"
      :inline="inline"
      :rules="rules"
      :label-width="labelWidth"
      class="form"
    >
      <el-form-item
        :label="item.title"
        :prop="item.key"
        v-for="item in options"
        :key="item.key"
      >
        <div v-if="$slots[item.key]">
          <slot :name="item.key"></slot>
        </div>
        <div v-else>
          <el-input
            :size="size"
            v-model="form[item.key]"
            v-if="item.type === 'text'"
            :placeholder="
              item.placeholder ? item.placeholder : '请输入' + item.title
            "
            :disabled="item.disabled"
            clearable
          ></el-input>
          <el-input
            :size="size"
            type="textarea"
            :autosize="{ minRows: 3, maxRows: 6 }"
            :placeholder="
              item.placeholder ? item.placeholder : '请输入' + item.title
            "
            v-model="form[item.key]"
            v-if="item.type === 'textarea'"
            :disabled="item.disabled"
          >
          </el-input>
          <el-switch
            :size="size"
            v-model="form[item.key]"
            v-if="item.type === 'switch'"
            active-color="#13ce66"
            inactive-color="#ff4949"
            :disabled="item.disabled"
          >
          </el-switch>
          <el-time-picker
            :size="size"
            :placeholder="
              item.placeholder ? item.placeholder : '请选择' + item.title
            "
            v-model="form[item.key]"
            value-format="HH:mm:ss"
            v-if="item.type === 'time'"
            :disabled="item.disabled"
          >
          </el-time-picker>
          <el-date-picker
            type="date"
            :placeholder="
              item.placeholder ? item.placeholder : '请选择' + item.title
            "
            value-format="yyyy-MM-dd"
            :size="size"
            v-model="form[item.key]"
            v-if="item.type === 'date'"
            :disabled="item.disabled"
          ></el-date-picker>
          <el-date-picker
            type="datetime"
            :placeholder="
              item.placeholder ? item.placeholder : '请选择' + item.title
            "
            value-format="yyyy-MM-dd HH:mm:ss"
            :size="size"
            v-model="form[item.key]"
            v-if="item.type === 'datetime'"
            :disabled="item.disabled"
          ></el-date-picker>
          <el-date-picker
            type="daterange"
            range-separator="至"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            value-format="yyyy-MM-dd HH:mm:ss"
            :size="size"
            v-model="form[item.key]"
            v-if="item.type === 'date-to-date'"
            :disabled="item.disabled"
          ></el-date-picker>
          <el-date-picker
            type="datetimerange"
            range-separator="至"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            value-format="yyyy-MM-dd HH:mm:ss"
            :size="size"
            v-model="form[item.key]"
            v-if="item.type === 'datetime-to-datetime'"
            :disabled="item.disabled"
          ></el-date-picker>
          <el-input-number
            :size="size"
            v-model="form[item.key]"
            v-if="item.type === 'number'"
            :disabled="item.disabled"
          ></el-input-number>
          <el-select
            :size="size"
            v-model="form[item.key]"
            v-if="item.type === 'select'"
            clearable
            :placeholder="
              item.placeholder ? item.placeholder : '请选择' + item.title
            "
            :disabled="item.disabled"
          >
            <el-option
              v-for="data in item.option.data"
              :key="data[item.option.props.key]"
              :label="data[item.option.props.label]"
              :value="data[item.option.props.value]"
            >
            </el-option>
          </el-select>
          <el-cascader
            :size="size"
            v-model="form[item.key]"
            v-if="item.type === 'cascader'"
            :disabled="item.disabled"
            :options="item.option.data"
            :props="item.option.props"
            clearable
          ></el-cascader>
          <el-radio-group
            :size="size"
            v-model="form[item.key]"
            v-if="item.type === 'radio'"
            :disabled="item.disabled"
          >
            <el-radio
              v-for="data in item.option.data"
              :key="data[item.option.props.key]"
              :label="data[item.option.props.value]"
              >{{ data[item.option.props.label] }}</el-radio
            >
          </el-radio-group>
          <el-checkbox-group
            :size="size"
            v-model="form[item.key]"
            v-if="item.type === 'checkbox'"
            :disabled="item.disabled"
          >
            <el-checkbox
              v-for="data in item.option.data"
              :key="data[item.option.props.key]"
              :label="data[item.option.props.label]"
              >{{ data[item.option.props.label] }}</el-checkbox
            >
          </el-checkbox-group>
          <el-upload
            v-if="item.type === 'file'"
            action="/api/upload_ajax"
            :show-file-list="false"
            :on-success="handleAvatarSuccess"
            :disabled="item.disabled"
          >
            <img
              v-if="form[item.key]"
              :src="form[item.key]"
              alt=""
              style="width: 178px; height: 178px; border-radius: 6px;"
              @click="setFileKey(item.key)"
            />
            <i
              v-else
              class="el-icon-plus avatar-uploader-icon"
              @click="setFileKey(item.key)"
            ></i>
          </el-upload>
        </div>
      </el-form-item>
      <slot name="options"></slot>
    </el-form>
  </div>
</template>

<script>
export default {
  name: "index",
  props: {
    // 表单
    form: {
      type: Object,
      default: function() {
        return {};
      }
    },
    // 表单配置
    options: {
      type: Array,
      default: function() {
        return [];
      }
    },
    // 表单验证规则
    rules: {
      type: Object,
      default: function() {
        return {};
      }
    },
    // 是否行内表单
    inline: {
      type: Boolean,
      default: function() {
        return false;
      }
    },
    // 表单表头宽度
    labelWidth: {
      type: String,
      default: function() {
        return "80px";
      }
    },
    // 控件大小
    size: {
      type: String,
      default: function() {
        return "medium";
      }
    }
  },
  data() {
    return {
      fileKey: ""
    };
  },
  mounted() {
    // 动态添加验证
    this.addRules(this.options);
    // 初始化完成清除一下表单验证规则
    this.$nextTick(function() {
      this.clearValidate();
    });
  },
  methods: {
    /**
     * @description: 动态添加表单验证
     * @author: chenbz
     * @date: 2021/4/27
     */
    addRules(options) {
      for (let i = 0; i < options.length; i++) {
        // 判断是否需要添加验证
        if (options[i].rules) {
          // 判断用户是否已经自定义验证了
          if (!this.rules[options[i].key]) {
            // 动态生成验证方法
            let data = [
              {
                required: true,
                message: "",
                trigger: ""
              }
            ];
            // 动态添加验证方式:失去焦点触发/确认时触发
            if (options[i].type === ("input" || "text")) {
              data[0].message = "请输入" + options[i].title;
              data[0].trigger = "blur";
            } else {
              data[0].message = "请选择" + options[i].title;
              data[0].trigger = "change";
            }
            // 触发vue更新
            this.$set(this.rules, options[i].key, data);
          }
        }
      }
    },
    /**
     * @description: 表单验证
     * @author: chenbz
     * @date: 2021/5/10
     */
    validate() {
      let isValidate = false;
      this.$refs.form.validate(valid => {
        if (valid) {
          isValidate = true;
        }
      });
      return isValidate;
    },
    /**
     * @description: 清除表单验证
     * @author: chenbz
     * @date: 2021/5/18
     */
    clearValidate() {
      this.$refs.form.clearValidate();
    },
    setFileKey(key) {
      // console.log(key);
      this.fileKey = key;
    },
    handleAvatarSuccess(res, file) {
      // console.log(res);
      // console.log(file);
      file;
      this.form[this.fileKey] = res.data;
    }
  }
};
</script>

<style scoped>
>>> .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  width: 178px;
  height: 178px;
}

.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}

>>> .el-upload:hover {
  border-color: #409eff;
}
</style>


# 引入页面

src/views/home.vue

<template>
  <div>
    <div>
      <MyForm
        :form="formData.form"
        :inline="true"
        :options="formData.options"
        :rules="formData.rules"
        :labelWidth="formData.labelWidth"
      ></MyForm>
      <el-divider></el-divider>
      <MyForm
        :form="formData.form"
        :options="formData.options"
        :rules="formData.rules"
        :labelWidth="formData.labelWidth"
      ></MyForm>
      <el-divider></el-divider>
    </div>
  </div>
</template>

<script>
import MyForm from "@/components/Form.vue";
export default {
  name: "home",
  components: {
    MyForm
  },
  data() {
    return {
      formData: {
        form: {
          date: "",
          sex: "",
          name: ""
        },
        options: [
          {
            title: "日期",
            key: "date",
            type: "date",
            rules: true
          },
          {
            title: "性别",
            key: "sex",
            type: "select",
            option: {
              data: [
                {
                  id: 1,
                  title: "女",
                  type: 0
                },
                {
                  id: 2,
                  title: "男",
                  type: 1
                }
              ],
              props: {
                key: "id", // 指定key对应data的字段
                label: "title", // 指定label对应data的字段
                value: "type" // 指定value对应data的字段
              }
            }
          },
          {
            title: "名字",
            key: "name",
            type: "input",
            rules: true
          }
        ],
        rules: {},
        labelWidth: "80px"
      }
    };
  },
  mounted() {},
  methods: {
  }
};
</script>

<style scoped></style>


# 自定义图片上传

Home.vue

src/views/Home.vue

<template>
  <div>
    <div>
      <MyForm
        :form="formData.form"
        :options="formData.options"
        :rules="formData.rules"
        :labelWidth="formData.labelWidth"
      >
        <template slot="options">
          <el-form-item label="文件上传">
            <el-upload
              action=""
              :show-file-list="false"
              :on-change="handleChange"
              :auto-upload="false"
            >
              <img v-if="formData.form.img" :src="formData.form.img" alt="" />
              <i v-else class="el-icon-plus avatar-uploader-icon"></i>
            </el-upload>
          </el-form-item>
        </template>
      </MyForm>
      <el-divider></el-divider>
    </div>
  </div>
</template>

<script>
import MyForm from "@/components/Form.vue";
export default {
  name: "Home",
  components: {
    MyForm
  },
  data() {
    return {
      formData: {
        form: {
          date: ""
        },
        options: [
        ],
        rules: {},
        labelWidth: "80px"
      }
    };
  },
  mounted() {},
  methods: {
    handleChange(file, fileList) {
      fileList;
      this.formData.form.img = URL.createObjectURL(file.raw);
    }
  }
};
</script>

<style></style>

使用 template 包含自定义的内容,slot一定要是options

上传组件根据业务而定,我预留了一个slot插槽用来显示自定义的组件



# 参数说明
属性描述默认值必需
formform表单{}
options表单配置[]
inline是否开启行内表单false
rules表单验证规则{}
labelWidth表单表头宽度80px
size控件大小medium


# options
属性描述必需
title表单表头
key表单对应的字段
type控件类型
rules是否开启验证
option表单配置
disabled是否禁用
placeholder自定义提示


# type
属性描述
input输入框
text文本输入框
switch开关
time时间
date日期
datetime日期时间
date-to-date开始日期 — 结束日期
datetime-to-datetime开始日期时间 — 结束日期时间
number数字
select选择框
cascader联级选择框
radio单选框
checkbox多选框
file图片上传

checkbox绑定字段的值一定要是: []

例如:sex: []



# option

只有在type[select, cascader, radio, checkbox]时才需要设置

属性描述必需
data数据源
props配置


# props

type[cascader] 的配置可以直接看官网

属性描述必需
Key指定唯一字段
label指定显示标题字段
value指定选中后值字段


# 方法
属性描述回调函数
validate()表单验证isValidate
clearValidate()清除表单验证


# 效果图



# 更多表单

Home.vue

src/views/Home.vue

<template>
  <div>
    <div>
      <MyForm
        :form="formData.form"
        :options="formData.options"
        :rules="formData.rules"
        :labelWidth="formData.labelWidth"
      >
      </MyForm>
      <el-divider></el-divider>
    </div>
  </div>
</template>

<script>
import MyForm from "@/components/Form.vue";
export default {
  name: "home",
  components: {
    MyForm
  },
  data() {
    return {
      formData: {
        form: {
          input: "",
          text: "",
          switch: true,
          time: "10:10:10",
          date: "2021-04-27",
          datetime: "2021-04-27 10:10:10",
          "date-to-date": "",
          "datetime-to-datetime": "",
          number: 0,
          select: "",
          radio: "",
          checkbox: []
        },
        options: [
          {
            title: "输入框",
            key: "input",
            type: "input"
          },
          {
            title: "文本",
            key: "text",
            type: "text"
          },
          {
            title: "开关",
            key: "switch",
            type: "switch"
          },
          {
            title: "时间",
            key: "time",
            type: "time"
          },
          {
            title: "日期",
            key: "date",
            type: "date"
          },
          {
            title: "日期时间",
            key: "datetime",
            type: "datetime"
          },
          {
            title: "开始日期 --- 结束日期",
            key: "date-to-date",
            type: "date-to-date"
          },
          {
            title: "开始日期时间 --- 结束日期时间",
            key: "datetime-to-datetime",
            type: "datetime-to-datetime"
          },
          {
            title: "数字",
            key: "number",
            type: "number"
          },
          {
            title: "选择框",
            key: "select",
            type: "select",
            option: {
              data: [
                {
                  id: 1,
                  title: "女",
                  type: 0
                },
                {
                  id: 2,
                  title: "男",
                  type: 1
                }
              ],
              props: {
                key: "id", // 指定key对应data的字段
                label: "title", // 指定label对应data的字段
                value: "type" // 指定value对应data的字段
              }
            }
          },
          {
            title: "单选框",
            key: "radio",
            type: "radio",
            option: {
              data: [
                {
                  id: 1,
                  title: "女",
                  type: 0
                },
                {
                  id: 2,
                  title: "男",
                  type: 1
                }
              ],
              props: {
                key: "id", // 指定key对应data的字段
                label: "title", // 指定label对应data的字段
                value: "type" // 指定value对应data的字段
              }
            }
          },
          {
            title: "多选框",
            key: "checkbox",
            type: "checkbox",
            option: {
              data: [
                {
                  id: 1,
                  title: "1"
                },
                {
                  id: 2,
                  title: "2"
                },
                {
                  id: 3,
                  title: "3"
                }
              ],
              props: {
                key: "id", // 指定key对应data的字段
                label: "title", // 指定label对应data的字段
              }
            }
          }
        ],
        rules: {},
        labelWidth: "250px"
      }
    };
  },
  mounted() {},
  methods: {}
};
</script>

<style></style>


# 效果图

Snipaste_2021-04-27_16-51-19

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值