• 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

    Quick Start

    pnpm i el-form-renderer-vue3
    
    • 1
    import elFormRenderer from "el-form-renderer-vue3";
    app.use(elFormRenderer);
    
    • 1
    • 2
    <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>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142

    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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201

    Methods

    support all el-form’s methods

    Slots

    SlotDescription
    defaultinsert at bottom
    id:helloinsert before form-item whose id is ‘hello’

    Inspiration

    thanks to el-form-renderer

  • 相关阅读:
    悲观锁和乐观锁、缓存
    go栈内存管理
    财政政策与货币政策
    数据库实验7 完整性约束
    【Tomcat】解决Tomcat服务器乱码问题
    CentOS安装IRIS
    netty系列之:kequeue传输协议详解
    【微机原理笔记】第 4 章 - 8086 汇编语言程序设计
    Android中的view绘制流程,简单理解
    idea实用快捷键(持续更新...)
  • 原文地址:https://blog.csdn.net/qq_63358859/article/details/134089266