index.vue 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. <template>
  2. <component :is="'el-form'" v-bind="_formOptions" ref="proFormRef" :model="formModel">
  3. <el-row :gutter="5">
  4. <template v-for="item in itemsOptions" :key="item.prop">
  5. <el-col :span="item.span || 24" g>
  6. <component :is="'el-form-item'" v-bind="item" v-if="show(item.show)">
  7. <template #label>
  8. <el-space :size="4">
  9. <span class="label-span">{{ `${item.label}` }}</span>
  10. <!-- <el-tooltip :content="item.formItem.label" placement="top" :disabled="isShowTooltip">
  11. <span class="label-span" @mouseenter="visibleTooltip">{{ `${item.formItem.label}` }}</span>
  12. </el-tooltip> -->
  13. <el-tooltip v-if="item?.tooltip" effect="dark" :content="item?.tooltip" placement="top">
  14. <i :class="'iconfont icon-yiwen'"></i>
  15. </el-tooltip>
  16. </el-space>
  17. <span>{{ `${_formOptions.labelSuffix}` }}</span>
  18. </template>
  19. <template v-if="item.compOptions.elTagName === 'slot'">
  20. <slot :name="item.prop" :form-model="formModel"></slot>
  21. </template>
  22. <template v-if="item.compOptions.elTagName === 'icon'">
  23. <SelectIcon v-model:icon-value="formModel[item.prop]" />
  24. </template>
  25. <Item v-else :item="item" :form-model="formModel" />
  26. </component>
  27. </el-col>
  28. </template>
  29. </el-row>
  30. <el-form-item v-if="_formOptions.hasFooter">
  31. <slot name="operation" :form-model="formModel" :model="formModel" :pro-form-ref="proFormRef">
  32. <el-button type="primary" v-if="_formOptions.showSubmitButton" @click="onSubmit(proFormRef)">{{
  33. _formOptions.submitButtonText
  34. }}</el-button>
  35. <el-button v-if="_formOptions.showResetButton" type="info" @click="resetForm(proFormRef)">
  36. {{ _formOptions.resetButtonText }}
  37. </el-button>
  38. <el-button v-if="_formOptions.showCancelButton" @click="emits('cancel')">
  39. {{ _formOptions.cancelButtonText }}
  40. </el-button>
  41. </slot>
  42. </el-form-item>
  43. </component>
  44. </template>
  45. <script setup lang="ts" name="ProForm">
  46. import { ref, computed, ComputedRef, watch, unref, provide } from 'vue'
  47. import type { FormInstance } from 'element-plus'
  48. import Item from '@/components/ProForm/components/Item.vue'
  49. import SelectIcon from '@/components/SelectIcon/index.vue'
  50. // import { handleProp } from '@/utils'
  51. // 表单整体配置项
  52. export interface ProFormProps {
  53. formOptions?: ProForm.FormOptions
  54. itemsOptions: ProForm.ItemsOptions[]
  55. model?: Record<ProForm.FormItem['prop'], ProForm.FormItem['value']>
  56. }
  57. // 表单的数据
  58. const formModel = ref<Record<string, any>>({})
  59. // 表单ref
  60. const proFormRef = ref<FormInstance>()
  61. // 默认值
  62. const props = withDefaults(defineProps<ProFormProps>(), {
  63. items: () => [],
  64. model: () => ({})
  65. })
  66. const show = (showFunction: any) => {
  67. if (!showFunction) return true
  68. if (typeof showFunction == 'function') {
  69. // 直接调用 showFunction 函数,并传入 formModel.value
  70. return showFunction(formModel.value)
  71. }
  72. // 如果 showFunction 不是函数,直接返回 true 显示该表单项
  73. return true
  74. }
  75. // 设置option默认值,如果传入自定义的配置则合并form配置项
  76. const _formOptions: ComputedRef<ProForm.FormOptions> = computed(() => {
  77. const form = {
  78. inline: false,
  79. labelPosition: 'right',
  80. labelWidth: 120,
  81. disabled: false,
  82. hasFooter: true,
  83. labelSuffix: ': ',
  84. showSubmitButton: true,
  85. submitButtonText: '提交',
  86. resetButtonText: '重置',
  87. cancelButtonText: '取消'
  88. }
  89. return Object.assign(form, props?.formOptions)
  90. })
  91. // 事件传递定义
  92. interface EmitEvent {
  93. (e: 'submit', params: any): void
  94. (e: 'reset'): void
  95. (e: 'cancel'): void
  96. }
  97. const enumMap = ref(new Map<string, { [key: string]: any }[]>())
  98. const setEnumMap = async ({ prop, compOptions }: ProForm.ItemsOptions) => {
  99. const props = compOptions?.enumKey || prop
  100. const enumValue = compOptions?.enum
  101. if (!enumValue) return
  102. // 如果当前 enumMap 存在相同的值 return
  103. if (enumMap.value.has(props!) && (typeof enumValue === 'function' || enumMap.value.get(props!) === enumValue)) return
  104. // 当前 enum 为静态数据,则直接存储到 enumMap
  105. if (typeof enumValue !== 'function') return enumMap.value.set(props!, unref(enumValue!))
  106. // 为了防止接口执行慢,而存储慢,导致重复请求,所以预先存储为[],接口返回后再二次存储
  107. enumMap.value.set(props!, [])
  108. // 当前 enum 为后台数据需要请求数据,则调用该请求接口,并存储到 enumMap
  109. let { data } = await enumValue()
  110. enumMap.value.set(props!, data)
  111. }
  112. // 注入 enumMap
  113. provide('enumMap', enumMap)
  114. // 处理表单项需要的参数
  115. props.itemsOptions.forEach(async item => {
  116. await setEnumMap(item)
  117. })
  118. const emits = defineEmits<EmitEvent>()
  119. // 根据items初始化model, 如果items有传值就用传递的model数据模型,否则就给上面声明的formModel设置相应的(key,value) [item.prop], item.value是表单的默认值(选填)
  120. watch(
  121. () => props.model,
  122. () => {
  123. props.itemsOptions.map((item: ProForm.ItemsOptions) => {
  124. // 如果类型为checkbox,默认值需要设置一个空数组
  125. const value = ['checkbox', 'transfer'].includes(item.compOptions.elTagName!) ? [] : ''
  126. props.model ? (formModel.value = props.model) : (formModel.value[item.prop] = item.value || value)
  127. })
  128. },
  129. { immediate: true }
  130. )
  131. // 提交按钮
  132. const onSubmit = (formEl: FormInstance | undefined) => {
  133. console.log('表单提交数据', formModel.value)
  134. if (!formEl) return
  135. formEl.validate(valid => {
  136. if (valid) {
  137. emits('submit', formModel.value)
  138. } else {
  139. }
  140. })
  141. }
  142. // 重置
  143. const resetForm = (formEl: FormInstance | undefined) => {
  144. if (!formEl) return
  145. formEl.resetFields()
  146. }
  147. // let isShowTooltip = ref(false)
  148. // const visibleTooltip = e => {
  149. // console.log('e', e)
  150. // isShowTooltip.value = e.target.offsetWidth + 18 - e.fromElement.clientWidth > 0 ? false : true //18为required *号引起的偏差。
  151. // console.log('isShowTooltip', isShowTooltip.value)
  152. // }
  153. // 暴露方法给父组件使用
  154. defineExpose({
  155. proFormRef,
  156. resetForm,
  157. onSubmit
  158. })
  159. </script>
  160. <style scoped lang="scss">
  161. @import './index.scss';
  162. </style>