el-dialog-form组件封装,真的好用,可以让你开发效率提高

发布时间 2023-06-09 17:52:20作者: 前端小菜鸡美哥

1、新建DialogForm.vue文件

<template>
  <div class="base-dialog">
    <el-dialog
      :type="type"
      :custom-class="customClass"
      :close-on-click-modal="closeOnClickModal"
      :visible="visible"
      :title="title"
      :width="width"
      :show-close="true"
      :before-close="beforeClose"
    >
      <!-- <el-card> -->
        <el-form
          ref="d-form"
          :model="form"
          :label-position="labelPosition"
          label-width="120px"
        >
          <el-row :gutter="20">
            <el-col
              v-for="(item, index) in items"
              v-show="item.hidden ? !item.hidden(form) : true"
              :key="index"
              :span="item.span || 20"
            >
              <el-form-item
                :label="item.label"
                :prop="item.prop"
                :rules="item.hidden && item.hidden(form) ? null : item.rules"
              >
                <el-tooltip
                  :disabled="!item.tip"
                  effect="dark"
                  :content="item.tip"
                  placement="top"
                >
                  <slot
                    v-if="item.elName === 'slot'"
                    :name="item.prop"
                    :form="form"
                  ></slot>
                  <el-component
                    v-else
                    v-model="form[item.prop]"
                    :el-name="item.elName"
                    :on="item.on || {}"
                    :attrs="item.attrs || {}"
                    :options="item.options || []"
                    :disabled="item.disabled ? item.disabled(form) : false"
                  ></el-component>
                </el-tooltip>
              </el-form-item>
            </el-col>
            <slot></slot>
          </el-row>
        </el-form>
      <!-- </el-card> -->

      <span slot="footer" class="dialog-footer">
        <el-button size="small" @click="onCancel">取 消</el-button>
        <el-button type="primary" size="small" @click="onSubmit"
          >确 定</el-button
        >
      </span>
    </el-dialog>
  </div>
</template>

<script>
// 验证器文档地址:https://github.com/yiminghe/async-validator
import elComponent from "@/lib/elComponent.js";
export default {
  name: "ElDialogForm",
  components: {
    elComponent
  },
  props: {
    closeOnClickModal: { type: Boolean, default: false }, // 是否可以点击关闭
    type: String, // 对话框类型:1.基础表单[base-dialog-form] 2.表格[base-dialog-table] 3.全屏 [base-dialog-fullscreen]
    // 是否显示dialog
    visible: {
      type: Boolean,
      default: false
    },
    // 标题
    title: {
      type: String,
      default: ""
    },
    // 宽度
    width: {
      type: String,
      default: ""
    },
    // 表单项
    items: {
      type: Array,
      default: () => []
    },
    // 表单值
    form: {
      type: Object,
      default: () => {
        return {};
      }
    },
    labelPosition: {
      type: String,
      default: "right"
    }
  },
  computed: {
    customClass() {
      let className = "";
      switch (this.type) {
        case "form":
          className = "base-dialog-form";
          break;
        case "table":
          className = "base-dialog-table";
          break;
        case "fullscreen":
          className = "base-dialog-fullscreen";
          break;
      }
      return className;
    }
  },
  created() {
    this.type === "fullscreen" && (this.fullscreen = true);
  },
  methods: {
    onSubmit() {
      this.$refs["d-form"].validate(valid => {
        if (valid) {
          this.$emit("submit", this.form);
        }
      });
    },
    onCancel() {
      this.$emit("update:visible", false);
      this.$emit("cancel");
      this.$refs['d-form'].resetFields();
    },
    close() {
      this.$emit("update:visible", false);
      this.$refs['d-form'].resetFields();
    },
    beforeClose() {
      this.$emit("update:visible", false);
      this.$refs['d-form'].resetFields();
    }
  }
};
</script>

<style lang="scss">
.el-form-item {
  margin-bottom: 15px !important;
}
.base-dialog {
  text-align: left;
  .el-dialog__wrapper {
    // overflow: hidden;
    .el-dialog {
      display: flex;
      flex-direction: column;
      .el-dialog__header {
        height: 40px;
        line-height: 40px;
        padding: 0px 20px;
        background: #eff3fa;
        color: #fff;
        border-bottom: 1px solid #dcdfe6;
        border-top-left-radius: 0;
        border-top-right-radius: 0;
        .el-dialog__title {
          font-size: 16px;
          // font-weight: bold;
        }
        .el-dialog__headerbtn {
          top: 12px;
        }
      }
      .el-dialog__body {
        flex: 1;
        overflow: auto;
        padding: 20px !important;
      }
      .el-dialog__footer {
        text-align: center;
        border-top: 1px solid #eee;
        padding: 8px 20px;
        background: #fefefe;
      }
    }
    .base-dialog-form {
      height: auto;
      margin-top: 15vh !important;
      .el-dialog__body {
        padding: 20px 20px 0 20px !important;
      }
      .el-dialog__footer {
        border: none;
        padding: 10px 20px 20px;
        background: none;
      }
      .custom-table {
        // 取消表格下边线
        tbody tr:last-child td {
          border-bottom: none !important;
        }
      }
    }
    .base-dialog-table {
      height: 90vh;
      margin-top: 5vh !important;
      .el-dialog__body {
      }
    }
    .base-dialog-fullscreen {
      height: 100vh;
      width: 100vw;
      .el-dialog__body {
        padding: 10px;
      }
    }
  }
}
.el-date-editor.el-input{
  width: 100% !important;
}
.el-select {
  width: 100% !important;
}
.el-range-editor.el-input__inner{
  width: 100% !important;
}
.el-cascader{
  width: 100% !important;
}
</style>

2、新建lib文件夹,文件夹下面创建三个文件,分别是defaultAttrs.js,elComponent.js,slots.js,代码分别如下

defaultAttrs.js

const map = {
  'el-time-picker': {
    'size': 'small',
    'placeholder': '请选择时间',
    'value-format': 'HH:mm:ss',
  },
  'el-date-picker': {
    'size': 'small',
    'placeholder': '请选择日期',
    'value-format': 'yyyy-MM-dd',
    'type': 'date',
    'start-placeholder': '开始日期',
    'end-placeholder': '结束日期',
  },
};
export function getDefaultAttrs(elName) {
  return (
    map[elName] || {
      size: 'small',
    }
  );
}

 

elComponent.js

// 获取默认attrs
import { getDefaultAttrs } from './defaultAttrs.js';
// 获取子组件
import { getChild } from './slots.js';
// 导出新组件
export default {
  props: ['value', 'elName', 'options', 'disabled'],
  name: 'el-component',
  render(h) {
    const self = this;
    const slots = Object.keys(this.$slots)
      .reduce((arr, key) => arr.concat(this.$slots[key]), [])
      .map(vnode => {
        vnode.context = this._self;
        return vnode;
      });
    // 获取子组件,如果有的话
    const child = getChild(this.elName, h, this.options);
    // 获取事件监听
    const events = this.$attrs.on || {};
    // 渲染函数中没有与 v-model 的直接对应——必须自己实现相应的逻辑
    events['input'] = function(event) {
      self.$emit('input', event);
    };
    // 参数传进来的attrs
    const attrsinset = this.$attrs.attrs || {};
    // 默认的attrs
    const defaultAttrs = getDefaultAttrs(this.elName);
    // 根据type渲染组件
    return h(
      `${this.elName}`,
      {
        on: events,
        props: {
          ...this.$props,
          ...attrsinset,
        },
        // 透传 scopedSlots
        scopedSlots: this.$scopedSlots,
        attrs: {
          ...attrsinset,
          ...defaultAttrs,
          disabled: this.disabled,
        },
      },
      [...slots, ...child],
    );
  },
};

 

slots.js

const map = {
    'el-radio-group': 'el-radio',
    'el-checkbox-group': 'el-checkbox',
    'el-select': 'el-option',
};
export function getChild(elName, h, options = []) {
    const arr = [];
    if (map[elName] !== undefined) {
        for (const i of options) {
            const label = elName === 'el-select' ? i.label : i.value;
            const value = elName === 'el-select' ? i.value : i.label;
            arr.push(
                h(
                    `${map[elName]}`,
                    {
                        props: {
                            label,
                            value,
                        },
                    },
                    [h('span', i.label)]
                )
            );
        }
    }
    return arr;
}

3、如何在页面调用,代码如下

//在main.js中全局注册组件
import DialogFrom from '@/components/Table/DialogForm.vue'
Vue.component('DialogFrom', DialogFrom)
<!-- 按钮触发弹窗 -->
<el-button type="primary" size="small" @click="dialog = true">打开表单</el-button>
<!-- 表单 -->
    <DialogFrom
      ref="DialogFrom"
      type="form"
      :visible.sync="dialog"
      title="表单标题"
      width="50%"
      :items="items"
      :form="form"
      @submit="onSubmit"
    >
      <template slot="image">
        <div
          class="upLoadPicBox"
          title="750*750px"
          @click="modalPicTap('1', 'image')"
        >
          <div v-if="form.image" class="pictrue">
            <img :src="form.image" />
          </div>
          <div v-else class="upLoad">
            <i class="el-icon-picture cameraIconfont" />
          </div>
        </div>
      </template>
    </DialogFrom>
//JS的data部分
form: {
        input: "",
        inputNumber: 0,
        switch: false,
        timePicker: "",
        datePicker: "",
        radioGroup: 1,
        checkboxGroup: [],
        select: [],
        checkbox: 1,
        datePickerTwo: "",
        image: "",
        cascader: []
      },
      dialog: false,
      items: [
        {
          elName: "el-input",
          span: 12,
          label: "普通输入",
          prop: "input",
          rules: [
            {
              required: true,
              message: "请输入名称",
              trigger: "blur"
            }
          ],
          attrs: {
            placeholder: "请输入名称"
          },
          hidden: function() {
            return false;
          },
          disabled: e => {
            if (this.form.checkbox == 1) {
              return true;
            } else {
              return false;
            }
          },
          on: {
            blur: e => {
              console.log(e);
            }
          }
        },
        {
          elName: "el-date-picker",
          span: 12,
          label: "日期选择",
          prop: "datePicker",
          attrs: {
            "value-format": "yyyy-MM-dd"
          }
        },
        {
          elName: "el-date-picker",
          span: 12,
          label: "日期范围选择",
          prop: "datePickerTwo",
          attrs: {
            type: "daterange"
          }
        },
        {
          elName: "el-time-picker",
          span: 12,
          label: "时间选择",
          prop: "timePicker",
          on: {}
        },
        {
          elName: "el-switch",
          span: 12,
          label: "开关",
          prop: "switch",
          on: {}
        },
        {
          elName: "slot",
          span: 12,
          label: "上传图片",
          prop: "image"
        },
        {
          elName: "el-radio-group",
          span: 12,
          label: "单选框组",
          prop: "radioGroup",
          tip: "",
          options: [
            {
              label: "苹果",
              value: 1
            },
            {
              label: "西瓜",
              value: 2
            }
          ],
          on: {
            change: e => {}
          }
        },
        {
          elName: "el-cascader",
          span: 12,
          label: "下拉级联选择",
          prop: "cascader",
          options: [],
          attrs: {
            placeholder: "请选择级联",
            multiple: false
          },
          on: {
            change: e => {
              console.log("-----------");
              console.log(e);
            }
          }
        },
        {
          elName: "el-select",
          span: 12,
          label: "下拉选择",
          prop: "select",
          options: [
            {
              label: "鼠标",
              value: 1
            },
            {
              label: "键盘",
              value: 2
            }
          ],
          attrs: {
            placeholder: "请输入名称",
            multiple: true
          },
          on: {
            change: e => {
              console.log("-----------");
              console.log(e);
            }
          }
        },
        {
          elName: "el-checkbox",
          span: 12,
          label: "选择",
          prop: "checkbox",
          attrs: {
            label: "我同意",
            "true-label": 1,
            "false-label": 0
          },
          on: {
            change: e => {
              console.log("-----------");
              console.log(e);
            }
          }
        },
        {
          elName: "el-checkbox-group",
          span: 12,
          label: "多选框组",
          prop: "checkboxGroup",
          options: [
            {
              label: "鼠标",
              value: 1
            },
            {
              label: "键盘",
              value: 2
            },
            {
              label: "电脑",
              value: 3
            }
          ],
          on: {
            change: e => {
              console.log("-----------");
              console.log(e);
            }
          }
        }
      ],
//JS的methods方法部分
modalPicTap(tit, type) {
      const _this = this;
      const attr = [];
      this.$modalUpload(function(img) {
        if (tit === "1" && type == "image") {
          _this.form.image = img[0];
        }
      }, tit);
    },
    onSubmit(form) {
      console.log(form);
    },