一、基本结构渲染

1.el-form在最外层

(1)el-form需要绑定model属性

遍历options,给model添加每个表单项prop属性,值为表单项的value

(2)给el-form绑定rules属性

遍历表单项,给rules添加prop属性,值为表单项的rules

2.循环遍历options创建el-form-item标签

单独绑定form-item常用属性(prop,label,rules),通过v-bind一并绑定组件特有属性

3.使用component动态组件渲染element-plus组件,并绑定属性

(1)利用is属性,动态渲染element组件

(2)绑定必传value

二、数据绑定

1.给el-form组件绑定model和rules属性

2.给component绑定model属性

三、渲染有子元素的组件

1.添加children属性

2.form组建中,进行条件渲染

(1)根据children属性,进行条件渲染

(2)有children时,component中间通过默认插槽放入子组件

四、监听prop.options

监听prop.options,更新渲染表单,为动态删减表单做准备

五、利用插槽加上表单操作项

1.form组件中加入一个具名插槽,用于渲染操作项

2.给插槽定义prop,为父组件提供表单实例、表单数据

3.difenExpose

想外界暴露属性,父组件可通过ref方式获取到子组件实例,调用该方法

  1. <template>
  2. <el-form
  3. ref="form"
  4. v-if="model"
  5. :validate-on-rule-change="false"
  6. :model="model"
  7. :rules="rules"
  8. v-bind="$attrs"
  9. >
  10. <template v-for="(item, index) in options" :key="index">
  11. <el-form-item
  12. v-if="!item.children || !item.children!.length"
  13. :prop="item.prop"
  14. :label="item.label"
  15. >
  16. <component
  17. v-if="item.type !== 'upload' && item.type !== 'editor'"
  18. :placeholder="item.placeholder"
  19. v-bind="item.attrs"
  20. :is="`el-${item.type}`"
  21. v-model="model[item.prop!]"
  22. ></component>
  23. <div id="editor" v-if="item.type === 'editor'"></div>
  24. </el-form-item>
  25. <el-form-item
  26. v-if="item.children && item.children.length"
  27. :prop="item.prop"
  28. :label="item.label"
  29. >
  30. <component
  31. :placeholder="item.placeholder"
  32. v-bind="item.attrs"
  33. :is="`el-${item.type}`"
  34. v-model="model[item.prop!]"
  35. >
  36. <component
  37. v-for="(child, i) in item.children"
  38. :key="i"
  39. :is="`el-${child.type}`"
  40. :label="child.label"
  41. :value="child.value"
  42. ></component>
  43. </component>
  44. </el-form-item>
  45. </template>
  46. <el-form-item>
  47. <slot name="action" :form="form" :model="model"></slot>
  48. </el-form-item>
  49. </el-form>
  50. </template>
  51. <script lang='ts' setup>
  52. import { PropType, ref, onMounted, watch, nextTick } from 'vue'
  53. import { FormInstance, FormOptions } from './types/types'
  54. import cloneDeep from 'lodash/cloneDeep'
  55. import E from "wangeditor"
  56. let props = defineProps({
  57. // 表单的配置项
  58. options: {
  59. type: Array as PropType<FormOptions[]>,
  60. required: true
  61. },
  62. // 用户自定义上传方法
  63. httpRequest: {
  64. type: Function
  65. }
  66. })
  67. let model = ref<any>(null)
  68. let rules = ref<any>(null)
  69. let form = ref<FormInstance | null>()
  70. let edit = ref()
  71. // 初始化表单
  72. let initForm = () => {
  73. if (props.options && props.options.length) {
  74. let m: any = {}
  75. let r: any = {}
  76. props.options.map((item: FormOptions) => {
  77. m[item.prop!] = item.value
  78. r[item.prop!] = item.rules
  79. })
  80. model.value = cloneDeep(m)
  81. rules.value = cloneDeep(r)
  82. }
  83. }
  84. // 重置表单
  85. let resetFields = () => {
  86. // 重置element-plus的表单
  87. form.value!.resetFields()
  88. // 重置富文本编辑器的内容
  89. // 获取到富文本的配置项
  90. if (props.options && props.options.length) {
  91. let editorItem = props.options.find(item => item.type === 'editor')!
  92. edit.value.txt.html(editorItem.value)
  93. }
  94. }
  95. // 表单验证方法
  96. let validate = () => {
  97. return form.value!.validate
  98. }
  99. // 获取表单数据
  100. let getFormData = () => {
  101. return model.value
  102. }
  103. // 分发方法
  104. defineExpose({
  105. resetFields,
  106. validate,
  107. getFormData
  108. })
  109. onMounted(() => {
  110. initForm()
  111. })
  112. // 监听父组件传递进来的options
  113. watch(() => props.options, () => {
  114. initForm()
  115. }, { deep: true })
  116. </script>
  117. <style lang='scss' scoped>
  118. </style>