vue + elementUI 实现二次封装表单

vue + elementUI 实现二次封装表单

  • 引言
  • 封装的表单组件
  • 在页面中使用

引言

后台管理系统中,经常会出现需要表单的情况,但是又不想每个页面都实现一个,这样不方便维护,因此封装了表单组件,方便在页面使用

封装的表单组件


<template>
  <div>
    <el-form
      :model="formData"
      :rules="rules"
      ref="ruleForm"
      :label-width="labelWidth"
      :label-position="labelPosition"
    >
      <el-form-item
        v-for="item in formConfig"
        :label="item.label"
        :prop="item.prop"
        :key="item.label"
      >
        
        <el-input
          v-if="item.type === 'input'"
          v-model="formData[item.prop]"
          :disabled="item.disabled"
          :style="{ width: item.width }"
          @change="item.change && item.change(formData[item.prop])"
          clearable
        >el-input>

        
        <el-input
          v-if="item.type === 'password'"
          type="password"
          v-model="formData[item.prop]"
          autocomplete="off"
          :style="{ width: item.width }"
          @change="item.change && item.change(formData[item.prop])"
          clearable
          show-password
        >el-input>

        
        <el-input
          v-if="item.type === 'textarea'"
          type="textarea"
          v-model="formData[item.prop]"
          :disabled="item.disabled"
          :style="{ width: item.width }"
          :maxlength="item.maxlength"
          :rows="item.rows"
          :show-word-limit="item.showWordLimit"
          @change="item.change && item.change(formData[item.prop])"
        >el-input>

        
        <el-select
          v-if="item.type === 'select'"
          v-model="formData[item.prop]"
          :disabled="item.disabled"
          :style="{ width: item.width }"
          @change="item.change && item.change(formData[item.prop])"
          clearable
        >
          <el-option
            v-for="option in item.options"
            :label="option.label"
            :value="option.value"
            :key="option.value"
          >el-option>
        el-select>

        
        <el-radio-group
          v-if="item.type === 'radio'"
          v-model="formData[item.prop]"
          :disabled="item.disabled"
          @change="item.change && item.change(formData[item.prop])"
        >
          <el-radio
            v-for="radio in item.radios"
            :label="radio.label"
            :key="radio.label"
          >el-radio>
        el-radio-group>

        
        <el-checkbox-group
          v-if="item.type === 'checkbox'"
          v-model="formData[item.prop]"
          :disabled="item.disabled"
          @change="item.change && item.change(formData[item.prop])"
        >
          <el-checkbox
            v-for="checkbox in item.checkboxs"
            :label="checkbox.label"
            :key="checkbox.label"
          >el-checkbox>
        el-checkbox-group>

        
        <el-switch
          v-if="item.type === 'switch'"
          v-model="formData[item.prop]"
          :disabled="item.disabled"
          @change="item.change && item.change(formData[item.prop])"
        >el-switch>

        
        <el-upload
          v-if="item.type === 'upload'"
          class="avatar-uploader"
          ref="uploadxls"
          :action="item.action"
          :limit="item.limit"
          :show-file-list="false"
          :on-success="handleSuccess"
          :on-change="handleFileChange"
          :before-upload="beforeUpload"
        >
          <slot name="upload">slot>
        el-upload>

        
        <el-cascader
          v-if="item.type === 'cascader'"
          size="large"
          v-model="formData[item.prop]"
          :options="options"
          :style="{ width: item.width }"
          @change="item.change && item.change(formData[item.prop])"
          clearable
        >
        el-cascader>

        
        <template v-if="item.type === 'slotName'">
          <slot :name="item.slotName">slot>
        template>
      el-form-item>
    el-form>
  div>
template>

<script lang="ts">
import Vue from 'vue';

export default Vue.extend({
  props: {
    // 表单域标签的宽度
    labelWidth: {
      type: String,
      default: '100px'
    },
    // 表单域标签的位置
    labelPosition: {
      type: String,
      default: 'right'
    },
    // 表单配置
    formConfig: {
      type: Array,
      required: true
    },
    // 表单数据
    formData: {
      type: Object,
      required: true
    },
    // 表单规则
    rules: {
      type: Object
    },
    // 级联数据
    options: {
      type: Array
    }
  },
  methods: {
    // 上传成功的方法
    handleSuccess (res: any, file: any) {
      this.$emit('uploadSuccess');
      console.log('ok');
    },
    // 文件状态改变时的方法
    handleFileChange (file: any, fileList: any) {
      // URL.createObjectURL(file.raw) 一般在上传成功的函数中,这里是为了测试方便
      this.$emit('uploadFileChange', URL.createObjectURL(file.raw));
      console.log(file, fileList);
    },
    // 上传文件之前的方法
    beforeUpload (file: any) {
      this.$emit('uploadbefore', file);
      console.log(file);
      // return false;
    },
    // 表单字段校验方法(自定义检验)
    validateField (valid: string) {
      (this.$refs as any).ruleForm.validateField(valid);
    },
    // 提交判断方法
    submitForm (callback: (arg0: any) => void) {
      (this.$refs as any).ruleForm.validate((valid: any) => {
        callback(valid);
      });
    },
    // 重置方法
    resetForm () {
      (this.$refs as any).ruleForm.resetFields();
    }
  }
});
script>

<style lang="less" scoped>
/deep/.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
/deep/.avatar-uploader .el-upload:hover {
  border-color: #409eff;
}
/deep/textarea {
  resize: none;
}
style>


在页面中使用

<template>
  <div class="form">
    <my-form
      :labelWidth="labelWidth"
      :formConfig="formConfig"
      :formData="formData"
      :rules="rules"
      :options="options"
      ref="ruleForm"
      @uploadFileChange="uploadFileChange"
      @uploadbefore="uploadbefore"
    >
      <template slot="upload">
        <img v-if="formData.sfz" :src="formData.sfz" class="avatar" />
        <i v-else class="el-icon-plus avatar-uploader-icon">i>
      template>

      <template slot="button">
        <el-button type="primary" @click.prevent="submitForm()">
          立即创建
        el-button>
        <el-button @click.prevent="resetForm()">重置el-button>
      template>
    my-form>
  div>
template>

<script lang="ts">
import Vue from 'vue';
import { regionData, CodeToText } from 'element-china-area-data';
import MyForm from '@/components/Form/index.vue';

export default Vue.extend({
  name: 'Form',
  data () {
    const validatePassword = (rule: any, value: string, callback: any) => {
      if (value === '') {
        callback(new Error('请输入密码'));
      } else {
        if ((this as any).formData.rePwd !== '') {
          (this.$refs as any).ruleForm.validateField('rePwd');
        }
        callback();
      }
    };
    const validaterePwd = (rule: any, value: string, callback: any) => {
      if (value === '') {
        callback(new Error('请再次输入密码'));
      } else if (value !== (this as any).formData.newPwd) {
        callback(new Error('两次输入密码不一致!'));
      } else {
        callback();
      }
    };
    const self = this;
    return {
      // 表单域标签的宽度
      labelWidth: '150px',
      // 表单配置
      formConfig: [
        {
          label: '活动名称',
          prop: 'name',
          type: 'input',
          width: '150px'
        },
        {
          label: '活动区域',
          prop: 'region',
          type: 'select',
          width: '200px',
          options: [
            {
              label: '区域一',
              value: 'shanghai'
            },
            {
              label: '区域二',
              value: 'beijing'
            }
          ]
        },
        {
          label: '即时配送',
          prop: 'delivery',
          type: 'switch'
        },
        {
          label: '活动性质',
          prop: 'type',
          type: 'checkbox',
          checkboxs: [
            {
              label: '线上品牌商赞助'
            },
            {
              label: '线下场地免费'
            }
          ]
        },
        {
          label: '特殊资源',
          prop: 'resource',
          type: 'radio',
          radios: [
            {
              label: '线上品牌商赞助'
            },
            {
              label: '线下场地免费'
            }
          ]
        },
        {
          label: '身份证正面照',
          prop: 'sfz',
          type: 'upload',
          action: '/',
          limit: 1
        },
        {
          label: '活动形式',
          prop: 'desc',
          type: 'textarea',
          width: '200px',
          rows: '5',
          maxlength: '30',
          showWordLimit: true
        },
        {
          label: '密码',
          prop: 'newPwd',
          type: 'password',
          width: '200px'
        },
        {
          label: '确认密码',
          prop: 'rePwd',
          type: 'password',
          width: '200px'
        },
        {
          label: '地址',
          prop: 'selectedOptions',
          type: 'cascader',
          width: '300px',
          change: (data: any) => {
            (self as any).changAddress(data);
          }
        },
        {
          type: 'slotName',
          slotName: 'button'
        }
      ],
      // 表单数据
      formData: {
        name: '',
        region: '',
        delivery: false,
        type: [],
        resource: '',
        sfz: '',
        desc: '',
        rePwd: '',
        newPwd: '',
        selectedOptions: []
      },
      // 表单规则
      rules: {
        name: [{ required: true, message: '请输入活动名称', trigger: 'blur' }],
        region: [
          { required: true, message: '请选择活动区域', trigger: 'change' }
        ],
        type: [
          {
            type: 'array',
            required: true,
            message: '请至少选择一个活动性质',
            trigger: 'change'
          }
        ],
        resource: [
          { required: true, message: '请选择活动资源', trigger: 'change' }
        ],
        sfz: [{ required: true, message: '请上传身份证' }],
        desc: [{ required: true, message: '请填写活动形式', trigger: 'blur' }],
        newPwd: [
          { required: true, validator: validatePassword, trigger: 'blur' }
        ],
        rePwd: [{ required: true, validator: validaterePwd, trigger: 'blur' }],
        selectedOptions: [{ required: true, message: '地址' }]
      },
      // 省市区三级联动数据
      options: regionData
    };
  },
  methods: {
    // 转化省市区为汉字
    changAddress (value: any) {
      console.log(CodeToText[value[0]]);
      console.log(CodeToText[value[1]]);
      console.log(CodeToText[value[2]]);
    },
    submitForm () {
      (this.$refs as any).ruleForm.submitForm((valid: any) => {
        if (valid) {
          console.log(this.formData);
          console.log('ok');
        } else {
          console.log('请填写表单的必要信息');
        }
      });
    },
    resetForm () {
      (this.$refs as any).ruleForm.resetForm();
    },
    uploadFileChange (localUrl: string) {
      console.log(localUrl);
      this.formData.sfz = localUrl;
    },
    uploadbefore (file: any) {
      const isJPG = file.type === 'image/jpeg';
      if (!isJPG) {
        this.$tipMessage('error', '上传头像图片只能是 JPG 格式!');
      }
    }
  },
  mounted () {
    console.log('form');
  },
  components: { MyForm }
});
script>

<style lang="less" scoped>
.form {
  height: 100%;
}
.avatar {
  width: 178px;
  height: 178px;
  display: block;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}
style>

文章仅供参考,具体需求根据项目更改

你可能感兴趣的:(vue,element-ui,TypeScript,elementui,vue.js,typescript)