vue + elementUI implements secondary encapsulation form

vue + elementUI implements secondary encapsulation form

introduction

In the background management system, there are often situations where a form is needed, but we don’t want to implement one on every page, which is inconvenient to maintain. Therefore, we encapsulate the form component to make it easier to use on the page.

Encapsulated form component

<!-- 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>


Use in the page

<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>

This article is for reference only, specific needs may change according to the project

Guess you like

Origin blog.csdn.net/m0_64344940/article/details/122543713