Loading

element-ui 表单组件的简单封装-2

form.vue

<template>
  <div class="form">
    <hr />
    <h1>form{{ formData }}</h1>
    <el-form
      ref="formRef"
      :model="formData"
      :label-width="labelWidth"
      :inline="true"
      :label-position="labelPosition"
      :label-suffix="labelSubffix"
      :size="size"
    >
      <el-form-item
        v-for="(item, index) in formItems"
        :key="index"
        :prop="item.prop"
        :rules="item.rules"
        :required="item.required"
      >
        <!-- 标签设置 -->
        <template #label>
          <template v-if="item.tip">
            <el-tooltip
              class="item"
              effect="dark"
              :content="item.tip"
              placement="top"
            >
              <span
                >{{ item.label }}
                <i class="el-icon-warning el-icon--right"></i>
              </span>
            </el-tooltip>
          </template>
          <template v-else>
            {{ item.label }}
          </template>
        </template>

        <template v-if="item.type == 'input'">
          <el-input
            v-model="formData[item.prop]"
            :placeholder="item.placeholder"
            :disabled="item.disabled"
          ></el-input>
        </template>
        <template v-else-if="item.type == 'select'">
          <!-- 有的时候我们需要获取到下拉的字段以及所选到的值 -->
          <el-select
            v-model="formData[item.prop]"
            :placeholder="item.placeholder"
            clearable
            filterable
            @change="(value) => selectChange(item.prop, value)"
            :disabled="item.disabled"
            :remote="item.remote"
            :remote-method="(value) => selectRemoteMethod(item.prop, value)"
          >
            <el-option
              v-for="option in item.options"
              :key="option.value"
              :label="option.label"
              :value="option.value"
            >
            </el-option>
          </el-select>
        </template>
        <template v-else-if="item.type == 'date'">
          <el-date-picker
            v-model="formData[item.prop]"
            type="date"
            placeholder="选择日期"
            value-format="yyyy-MM-dd"
            :disabled="item.disabled"
          >
          </el-date-picker>
        </template>
        <template v-else-if="item.type == 'upload'">
          <!-- 向回调函数中传递更多的参数 -->
          <el-upload
            drag
            action="https://jsonplaceholder.typicode.com/posts/"
            :multiple="item.multiple"
            :auto-upload="false"
            :file-list="formData[item.prop]"
            :list-type="item.listType"
            :on-change="
              (file, fileList) => {
                fileOnChange(item.prop, file, fileList)
              }
            "
            :disabled="item.disabled"
          >
            <i class="el-icon-upload"></i>
            <div class="el-upload__text">
              将文件拖到此处,或<em>点击上传</em>
            </div>
            <div class="el-upload__tip" slot="tip">
              只能上传jpg/png文件,且不超过500kb
            </div>
          </el-upload>
        </template>
      </el-form-item>
    </el-form>
    <div class="footer" v-if="showFooter">
      <el-button type="primary" icon="el-icon-search" @click="resetForm"
        >重置</el-button
      >
      <el-button
        type="primary"
        icon="el-icon-upload
"
        @click="submitForm"
        >提交</el-button
      >
    </div>
  </div>
</template>

<script>
export default {
  name: 'MyForm',
  // 自定义组件上使用v-model
  //   1. 定义model。  2. 需要将model中prop值同时在props中定义
  //   父组件通过v-model指令绑定值时会将数据传递给子组件中的modelValue,子组件通过触发"update:modelValue"事件可以通知父组件
  model: {
    prop: 'modelValue',
    event: 'update:modelValue',
  },
  props: {
    modelValue: Object,
    formItems: {
      type: Array,
      required: true,
    },
    labelWidth: {
      type: String,
    },
    labelPosition: {
      // 自定义校验规则
      validator: (value) => {
        return ['left', 'right', 'top'].includes(value)
      },
    },
    labelSubffix: {
      type: String,
    },
    size: {
      validator: (value) => {
        return ['medium', 'small', 'mini'].includes(value)
      },
    },
    showFooter: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      formData: {},
    }
  },
  emits: ['update:modelValue', 'select-change', 'select-remote-search'],
  created() {
    // 表单初始化
    for (let item of this.formItems) {
      if (item.type == 'upload') {
        this.$set(this.formData, item.prop, [])
      } else {
        // 注意:这里要进行响应式的设置,否则表单控件会无法输入的
        // 如果有默认值则展示默认值
        this.$set(this.formData, item.prop, item.value ? item.value : '')
      }
    }
    this.$emit('update:modelValue', this.formData)
  },
  methods: {
    fileOnChange(field, file, fileList) {
      this.$set(this.formData, field, fileList)
    },
    resetForm() {
      this.$refs.formRef.resetFields()
    },
    submitForm() {
      this.$refs.formRef.validate().then((res) => {
        console.log(res)
      })
    },
    selectChange(field, value) {
      this.$emit('select-change', {
        field,
        value,
      })
      console.log(field, value)
    },
    // 远程搜索
    selectRemoteMethod(field, value) {
      this.$emit('select-remote-search', { field, value })
    },
  },
}
</script>

<style scoped>
.footer {
  display: flex;
  justify-content: center;
}
</style>

test.vue

<template>
  <div>
    test:{{ formData }}
    <hr />
    <my-form
      v-bind="formConfig"
      v-model="formData"
      @select-change="handleSelectChange"
      @select-remote-search="handleSelectRemoteSearch"
    ></my-form>
  </div>
</template>

<script>
import MyForm from '@/components/form'
import formConfig from './formConfig'
export default {
  data() {
    return {
      formConfig,
      formData: {},
    }
  },
  components: {
    MyForm,
  },
  methods: {
    handleSelectRemoteSearch({ field, value }) {
      console.log(`发起请求,根据${value}获取${field}字段的远程搜索`)
    },
    handleSelectChange({ field, value }) {
      console.log(
        `下拉项中${field}字段发现变化,根据${value}去变化其他的表单项,比如:省市联动`
      )
    },
  },
}
</script>

form.config.js

const formConfig = {
  formItems: [
    {
      type: 'input',
      prop: 'name',
      label: '姓名:',
      placeholder: '请输入姓名',
      rules: [{ required: true, message: '请输入活动名称', trigger: 'blur' }],
      disabled: true,
    },
    {
      type: 'input',
      prop: 'age',
      label: '年龄:',
      placeholder: '请输入年龄',
      required: true,
      rules: [
        {
          validator: (rule, value, cb) => {
            if (value === '') {
              cb(new Error('请输入年龄'))
            } else {
              if (isNaN(value)) {
                cb(new Error('请输入数字'))
              } else {
                if (value < 18) {
                  cb(new Error('年龄要大于18岁'))
                } else {
                  cb()
                }
              }
            }
          },
          trigger: 'blur',
        },
      ],
    },
    {
      type: 'select',
      prop: 'address',
      label: '地址:',
      placeholder: '请输入地址',
      options: [
        {
          label: '广州市',
          value: 'gz',
        },
        {
          label: '上海市',
          value: 'sh',
        },
        {
          label: '深圳市',
          value: 'sz',
        },
      ],
    },
    {
      type: 'date',
      prop: 'birthday',
      label: '生日:',
      placeholder: '请输入生日',
      tip: '祝你生日快乐',
      value: '2010-09-01',
    },
    {
      type: 'upload',
      prop: 'uploadData1',
      label: '照片:',
      placeholder: '请上传照片',
      rules: [{ required: true, message: '请上传照片', trigger: 'blur' }],
      listType: '.jpg',
    },
    {
      type: 'select',
      prop: 'movie',
      label: '电影:',
      placeholder: '请输入电影',
      rules: [{ required: true, message: '请输入电影', trigger: 'blur' }],
      tip: '输入内容进行远程搜索',
      remote: true,
    },
  ],
  labelWidth: '120px',
  labelPosition: 'right',
  // labelSubffix: '@',
  size: 'mini',
}

export default formConfig

posted @ 2023-03-06 23:37  ^Mao^  阅读(196)  评论(1编辑  收藏  举报