element-plus form表单的二次封装

个人简介

博主写了对element-plus的表格和表单的封装 大家支持一下
[表格]https://gitee.com/childe-jia/table-vue3
[表单] https://gitee.com/childe-jia/form-render

遗留问题 :待解决

select 为 multiple 多选时 必须初始化空数组(在 elementplus v-model 初始化 updateValue 时 为空数组会触发校验)

Introduction

WHAT

form-renderer 基于元素 element-plus,但不限于元素 element-plus 组件。在完全继承 element-plus 元素的 form 属性的基础上,进行了扩展。一些非表单组件或自定义组件,因此,用户可以使用一段 json 来呈现完整的表单。

WHY

在我们的日常开发中,有很多有表单的页面,通常表单结构相似,逻辑重复。el 表单呈现器没有复杂的逻辑。它只转换 JSON 来呈现表单项,节省了编写业务逻辑的时间和精力,并减少了重复代码。

Features

  • 用 json 呈现表单
  • 支持与自定义组件集成
  • 支持 updateForm 方法批量更新表单数据
  • 支持 setOptions 方法,动态更改选择选项
  • 内容支持 inputFormat、outputFormat、trim 以处理组件的输入和输出值
  • 支持 v-model

Links

  • $attrs 和 $listeners (vue2&&vue3)
  • vue2 与 vue3 函数式组件
  • vue 2 与 vue3 获取模版引用 (ref)的区别
  • vue2 与 vue3 v-model 的区别
  • vue2 版本
  • 中文文档
  • element-plus table 的封装

Quick Start

pnpm i el-form-renderer-vue3
import elFormRenderer from "el-form-renderer-vue3";
app.use(elFormRenderer);
<template>
  <el-form-renderer
    label-width="100px"
    :content="content"
    v-model:FormData="FormData"
    ref="form"
  >
    <el-button @click="resetForm">reset</el-button>
    <el-button @click="setValue">设置名字为小明</el-button>
    <pre>{{ FormData }}</pre>
  </el-form-renderer>
</template>

<script setup>
import { reactive, ref } from "vue";

const form = ref();
let FormData = reactive({
  name: "",
  type: [],
  startDate: "2019-01-01",
  endDate: "2019-01-02",
  region: [],
  date: ["2019-01-01", "2019-01-02"],
});
const content = reactive([
  {
    type: "input",
    id: "name",
    label: "name",
    attrs: { "data-name": "form1" },
    el: {
      size: "default",
      placeholder: "test placeholder",
    },
    rules: [
      { required: true, message: "miss name", trigger: "blur" },
      { min: 3, max: 5, message: "length between 3 to 5", trigger: "blur" },
    ],
  },
  {
    type: "select",
    id: "region",
    label: "region",
    options: [
      {
        label: "shanghai",
        value: "shanghai",
      },
      {
        label: "beijing",
        value: "beijing",
      },
      {
        label: "guangzhou",
        value: "guangzhou",
      },
    ],
    el: { filterable: true, multiple: true, multipleLimit: 2 },
    rules: [{ required: true, message: "miss area", trigger: "change" }],
  },
  {
    type: "date-picker",
    id: "date",
    label: "date",
    el: {
      type: "daterange",
      valueFormat: "yyyy-MM-dd",
    },
    rules: [{ required: true, message: "miss date", trigger: "change" }],
    inputFormat: (row) => {
      if (row.startDate && row.endDate) {
        return [row.startDate, row.endDate];
      }
    },
    outputFormat: (val) => {
      if (!val) {
        return { startDate: "", endDate: "" };
      }
      return {
        startDate: val[0],
        endDate: val[1],
      };
    },
  },
  {
    type: "switch",
    id: "delivery",
    label: "delivery",
  },
  {
    type: "checkbox-group",
    id: "type",
    label: "type",
    default: [],
    options: [
      {
        label: "typeA",
      },
      {
        label: "typeB",
      },
      {
        label: "typeC",
      },
    ],
    rules: [{ type: "array", required: true, message: "miss type", trigger: "change" }],
  },
  {
    type: "radio-group",
    id: "resource",
    label: "resource",
    options: [
      {
        label: "resourceA",
        value: "A",
      },
      {
        label: "resourceB",
        value: "B",
      },
    ],
    rules: [{ required: true, message: "miss resource", trigger: "change" }],
  },
  {
    type: "input",
    id: "desc",
    label: "desc",
    el: {
      type: "textarea",
    },
    rules: [{ required: true, message: "miss desc", trigger: "blur" }],
  },
]);
const resetForm = () => {
  form.value.methods.resetFields();
};
const setValue = () => {
  FormData.name = "小明";
};
</script>

Props

export default {
  // ...
  props: {
    /**
     * support all el-form's props
     * @see: https://element.eleme.io/#/zh-CN/component/form#form-attributes
     */

    /**
     * 表单项的配置数组,每个表单项代表一个原子表单项
     * the form config's array, each item represents a form-item
     */
    content: {
      type: Array, // type:Content[], check Content's definition below
      required: true
    },

    /**
     * disable all form-items
     */
    disabled: {
      type: Boolean,
      default: false
    }
  }
}

/**
 * 表单项的typescript定义
 * 支持所有el-form-item's props。表单项组件本身的props定义在el上
 * definition of form-item written in typescript.
 * support all el-form-item's props. The component's props need to be set at prop el
 */
interface Content {
  // 每一个原子都存在 id,用于存储该原子的值,不能重复
  // key of form-item value in form value. Must be unique
  id: string

  /**
   * 可以是element提供的所有表单组件类型,如传入'input',则渲染出'el-input'
   * 当type="group"时,可以创造复杂对象类型的表单数据,配合items使用。此时getFormValue()返回的是对象类型的数据,对象的每个属性对应items里的每一项
   * support all element's form component, e.g., type 'input' will render as 'el-input'.
   * you can create nested form value with type 'group' and use items to define that form value's shape. The type of this form value will be 'object'
   */
  type: string

  /**
   * 当type="group"时使用
   * items内依然遵循同一层级的id不重复的原则
   * using with type 'group'
   * the `id` in each item of items must be unique
   */
  items: Content[]

  /**
   * 默认值
   * FIXME: 别用关键字做 key
   */
  default?: any

  /**
   * 当 type === 'input' 时展示文本值
   * 当 type === 'select' 时展示对应 label
   * 对于其他组件等同于 disabled = true
   */
  readonly = false

  /**
   * @deprecated
   */
  enableWhen?: object | string

  /**
   * 传入一个方法,并返回 boolean,返回 true 时则隐藏该表单项
   * formValue 为当前 form 的值,item 为当前表单项的定义
   * hide the form-item when return true
   * formValue is same as what getFormValue returns, and item is the config of this form-item
   */
  hidden?: (formValue: Object, item: Content) => boolean

  /**
   * 具有选择功能的原子表单可用此定义可选项
   * use with type: select, radio-group, radio-button, checkbox-group, checkbox-button
   */
  options?: {label: string; value?: any}[]

  /**
   * 配置remote.url,即可远程配置组件的某个prop!
   * remote接受以下属性:
   * url: 远程接口的地址
   * prop: 要注入的 prop 的名称,默认为 options
   * request: 可选,请求方法
   * dataPath: 可选,data在响应体中的路径
   * onResponse: 可选,处理请求回来的数据
   * onError: 可选,处理请求出错的情况
   * 另外,针对 select、radio-group、checkbox-group,远程数据能自动映射成 el-option 选项!以下属性仅在此情况使用
   * label: 可选,可直接配置远程数据中用作 label 的key
   * value: 可选,可直接配置远程数据中用作 value 的key
   * @see https://zhuanlan.zhihu.com/p/97827063
   *
   * use remote to set one prop! remote accept following props:
   * url: remote api address
   * prop: prop name that data inject
   * request: optional, customize how to get your options
   * dataPath: optional, data's path in response
   * onResponse: optional, deal with your response
   * onError: optional, deal with request error
   * and, we treat select、radio-group、checkbox-group specially and the resp will be map as an el-option's group! following props only suitable for this case
   * label: optional, label key in resp
   * value: optional, value key in resp
   */
  remote?: {
    url: string
    request = () => this.$axios.get(url).then(resp => resp.data)
    prop = 'options'
    dataPath = ''
    onResponse = resp => {
      if (dataPath) resp = _get(resp, dataPath)
      switch (this.data.type) {
        case 'select':
        case 'checkbox-group':
        case 'radio-group':
          return resp.map(item => ({
            label: item[label],
            value: item[value]
          }))
        default:
          return resp
      }
    }
    onError = error => console.error(error.message)
    label = 'label'
    value = 'value'
  }

  attrs?: object // html attributes
  /**
   * 用于定义具体原子表单(如el-input)的属性,比如定义el-input的placeholder
   * use to define props of the actual component of this form-item, such as placeholder of el-input
   */
  el?: object

  /**
   * 使用自定义组件
   * component适用于渲染局部注册组件和自定义组件,而type适用于带el-前缀的全局组件
   * custom component
   * use it when element's form components are not enough
   */
  component?: Vue

  /**
   * 是否覆盖自定义组件内置的校验规则
   * `true` 为覆盖, 默认为 `false`
   * whether to override the validation rules written in custom components
   * `true` to override, default `false`
   */
  overrideRules: boolean

  label?: string //set el-form-item's label
  trim = true // trim value at change event

  // 用于处理输入值,输入的值包括:1. default;2. v-model;3. updateForm。参数为整个表单的值对象或 updateForm 传入的对象
  // 如果 inputFormat 返回 undefined,则不会更新此表单项
  // obj is param you passed to updateForm. You can use this function to hijack this process and customize the form value
  inputFormat?: (obj: any) => any

  // 用于处理输出值,参数为对应组件返回值
  // 如果处理后的值是对象类型,会覆盖(Object.assign)到整个表单的值上
  // used to hijack the getFormValue's process and customize the return value
  outputFormat?: (val: any) => any

  // set el-form-item's rules
  rules?: object

  // @deprecated
  atChange?: (id: string, value: any) => void

  /**
   * 监听表单项发出的事件
   * listen to any events emitted by component of form item
   * @param {any[]} args - what that event emits
   * @param {function} updateForm - same as methods.updateForm
   */
  on?: {
    [eventName: string]: (args: any[], updateForm: function) => void
  }
}

/**
 * a tour of typescript
 */
interface obj {
  a: string // type string
  b?: string // type string, optional
  c = true // type boolean, optional, default true
  d: string[] // type array, each item must be string
  e: any // could be any valid js type
  f: (a: number) => void // type function, which receives a param 'a' as number and return nothing
  h: Vue // instance of Vue
  i: {[a: string]: number} // type object, whose key is type string, and value is type number
}

Methods

support all el-form’s methods

Slots

Slot Description
default insert at bottom
id:hello insert before form-item whose id is ‘hello’

Inspiration

thanks to el-form-renderer

你可能感兴趣的:(前端技能,elementui)