vue + elementUI 实现二次封装表单

vue + elementUI 实现二次封装表单

引言

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

封装的表单组件

<!-- components/Form/index.vue -->
<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>

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

猜你喜欢

转载自blog.csdn.net/m0_64344940/article/details/122543713